umap-project 1.14.0a5__py3-none-any.whl → 2.0.0a1__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 (216) hide show
  1. umap/__init__.py +1 -1
  2. umap/decorators.py +0 -14
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/br/LC_MESSAGES/django.po +137 -85
  5. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/cs_CZ/LC_MESSAGES/django.po +136 -84
  7. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/el/LC_MESSAGES/django.po +136 -84
  9. umap/locale/en/LC_MESSAGES/django.po +128 -88
  10. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/es/LC_MESSAGES/django.po +136 -84
  12. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/fr/LC_MESSAGES/django.po +131 -91
  14. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/hu/LC_MESSAGES/django.po +137 -85
  16. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/it/LC_MESSAGES/django.po +136 -84
  18. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/ms/LC_MESSAGES/django.po +136 -84
  20. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/pl/LC_MESSAGES/django.po +136 -84
  22. umap/locale/sv/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/sv/LC_MESSAGES/django.po +135 -83
  24. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/zh_TW/LC_MESSAGES/django.po +143 -91
  26. umap/models.py +23 -1
  27. umap/settings/__init__.py +1 -4
  28. umap/settings/base.py +1 -0
  29. umap/static/umap/base.css +5 -0
  30. umap/static/umap/content.css +185 -13
  31. umap/static/umap/favicons/icon.svg +2 -2
  32. umap/static/umap/img/edit.svg +3 -3
  33. umap/static/umap/img/icon-delete.svg +4 -0
  34. umap/static/umap/img/icon-download.svg +13 -0
  35. umap/static/umap/img/icon-duplicate.svg +5 -0
  36. umap/static/umap/img/icon-edit.svg +12 -0
  37. umap/static/umap/img/icon-share.svg +11 -0
  38. umap/static/umap/img/icon-view.svg +12 -0
  39. umap/static/umap/img/logo.svg +2 -2
  40. umap/static/umap/img/logo_small.svg +2 -2
  41. umap/static/umap/img/marker.svg +4 -0
  42. umap/static/umap/img/opensource.svg +2 -2
  43. umap/static/umap/img/osm.svg +2 -2
  44. umap/static/umap/js/components/fragment.js +1 -1
  45. umap/static/umap/js/modules/browser.js +159 -0
  46. umap/static/umap/js/modules/global.js +3 -1
  47. umap/static/umap/js/modules/request.js +155 -0
  48. umap/static/umap/js/umap.autocomplete.js +28 -38
  49. umap/static/umap/js/umap.controls.js +73 -58
  50. umap/static/umap/js/umap.core.js +4 -9
  51. umap/static/umap/js/umap.datalayer.permissions.js +13 -12
  52. umap/static/umap/js/umap.features.js +51 -49
  53. umap/static/umap/js/umap.forms.js +19 -19
  54. umap/static/umap/js/umap.icon.js +17 -17
  55. umap/static/umap/js/umap.importer.js +2 -1
  56. umap/static/umap/js/umap.js +242 -291
  57. umap/static/umap/js/umap.layer.js +173 -141
  58. umap/static/umap/js/umap.permissions.js +24 -25
  59. umap/static/umap/js/umap.popup.js +14 -14
  60. umap/static/umap/js/umap.share.js +4 -4
  61. umap/static/umap/js/umap.slideshow.js +4 -4
  62. umap/static/umap/js/umap.tableeditor.js +2 -2
  63. umap/static/umap/js/umap.ui.js +1 -1
  64. umap/static/umap/locale/am_ET.js +1 -11
  65. umap/static/umap/locale/am_ET.json +1 -11
  66. umap/static/umap/locale/ar.js +1 -11
  67. umap/static/umap/locale/ar.json +1 -11
  68. umap/static/umap/locale/ast.js +1 -11
  69. umap/static/umap/locale/ast.json +1 -11
  70. umap/static/umap/locale/bg.js +1 -11
  71. umap/static/umap/locale/bg.json +1 -11
  72. umap/static/umap/locale/br.js +1 -11
  73. umap/static/umap/locale/br.json +1 -11
  74. umap/static/umap/locale/ca.js +1 -11
  75. umap/static/umap/locale/ca.json +1 -11
  76. umap/static/umap/locale/cs_CZ.js +1 -11
  77. umap/static/umap/locale/cs_CZ.json +1 -11
  78. umap/static/umap/locale/da.js +1 -11
  79. umap/static/umap/locale/da.json +1 -11
  80. umap/static/umap/locale/de.js +1 -11
  81. umap/static/umap/locale/de.json +1 -11
  82. umap/static/umap/locale/el.js +1 -11
  83. umap/static/umap/locale/el.json +1 -11
  84. umap/static/umap/locale/en.js +1 -11
  85. umap/static/umap/locale/en.json +1 -11
  86. umap/static/umap/locale/en_US.json +1 -11
  87. umap/static/umap/locale/es.js +1 -11
  88. umap/static/umap/locale/es.json +1 -11
  89. umap/static/umap/locale/et.js +1 -11
  90. umap/static/umap/locale/et.json +1 -11
  91. umap/static/umap/locale/fa_IR.js +6 -16
  92. umap/static/umap/locale/fa_IR.json +6 -16
  93. umap/static/umap/locale/fi.js +1 -11
  94. umap/static/umap/locale/fi.json +1 -11
  95. umap/static/umap/locale/fr.js +1 -11
  96. umap/static/umap/locale/fr.json +1 -11
  97. umap/static/umap/locale/gl.js +1 -11
  98. umap/static/umap/locale/gl.json +1 -11
  99. umap/static/umap/locale/he.js +1 -11
  100. umap/static/umap/locale/he.json +1 -11
  101. umap/static/umap/locale/hr.js +1 -11
  102. umap/static/umap/locale/hr.json +1 -11
  103. umap/static/umap/locale/hu.js +1 -11
  104. umap/static/umap/locale/hu.json +1 -11
  105. umap/static/umap/locale/id.js +1 -11
  106. umap/static/umap/locale/id.json +1 -11
  107. umap/static/umap/locale/is.js +1 -11
  108. umap/static/umap/locale/is.json +1 -11
  109. umap/static/umap/locale/it.js +1 -11
  110. umap/static/umap/locale/it.json +1 -11
  111. umap/static/umap/locale/ja.js +1 -11
  112. umap/static/umap/locale/ja.json +1 -11
  113. umap/static/umap/locale/ko.js +1 -11
  114. umap/static/umap/locale/ko.json +1 -11
  115. umap/static/umap/locale/lt.js +1 -11
  116. umap/static/umap/locale/lt.json +1 -11
  117. umap/static/umap/locale/ms.js +1 -11
  118. umap/static/umap/locale/ms.json +1 -11
  119. umap/static/umap/locale/nl.js +1 -11
  120. umap/static/umap/locale/nl.json +1 -11
  121. umap/static/umap/locale/no.js +1 -11
  122. umap/static/umap/locale/no.json +1 -11
  123. umap/static/umap/locale/pl.js +1 -11
  124. umap/static/umap/locale/pl.json +1 -11
  125. umap/static/umap/locale/pl_PL.json +1 -11
  126. umap/static/umap/locale/pt.js +1 -11
  127. umap/static/umap/locale/pt.json +1 -11
  128. umap/static/umap/locale/pt_BR.js +1 -11
  129. umap/static/umap/locale/pt_BR.json +1 -11
  130. umap/static/umap/locale/pt_PT.js +1 -11
  131. umap/static/umap/locale/pt_PT.json +1 -11
  132. umap/static/umap/locale/ro.js +1 -11
  133. umap/static/umap/locale/ro.json +1 -11
  134. umap/static/umap/locale/ru.js +1 -11
  135. umap/static/umap/locale/ru.json +1 -11
  136. umap/static/umap/locale/sk_SK.js +1 -11
  137. umap/static/umap/locale/sk_SK.json +1 -11
  138. umap/static/umap/locale/sl.js +1 -11
  139. umap/static/umap/locale/sl.json +1 -11
  140. umap/static/umap/locale/sr.js +1 -11
  141. umap/static/umap/locale/sr.json +1 -11
  142. umap/static/umap/locale/sv.js +1 -11
  143. umap/static/umap/locale/sv.json +1 -11
  144. umap/static/umap/locale/th_TH.js +1 -11
  145. umap/static/umap/locale/th_TH.json +1 -11
  146. umap/static/umap/locale/tr.js +1 -11
  147. umap/static/umap/locale/tr.json +1 -11
  148. umap/static/umap/locale/uk_UA.js +1 -11
  149. umap/static/umap/locale/uk_UA.json +1 -11
  150. umap/static/umap/locale/vi.js +1 -11
  151. umap/static/umap/locale/vi.json +1 -11
  152. umap/static/umap/locale/vi_VN.json +1 -11
  153. umap/static/umap/locale/zh.js +1 -11
  154. umap/static/umap/locale/zh.json +1 -11
  155. umap/static/umap/locale/zh_CN.json +1 -11
  156. umap/static/umap/locale/zh_TW.Big5.json +1 -11
  157. umap/static/umap/locale/zh_TW.js +17 -27
  158. umap/static/umap/locale/zh_TW.json +17 -27
  159. umap/static/umap/map.css +2 -2
  160. umap/static/umap/nav.css +2 -1
  161. umap/static/umap/test/.eslintrc +0 -1
  162. umap/static/umap/test/Choropleth.js +29 -27
  163. umap/static/umap/test/DataLayer.js +207 -239
  164. umap/static/umap/test/Feature.js +33 -58
  165. umap/static/umap/test/Map.Export.js +11 -11
  166. umap/static/umap/test/Map.js +66 -67
  167. umap/static/umap/test/Marker.js +36 -32
  168. umap/static/umap/test/Polygon.js +95 -95
  169. umap/static/umap/test/Polyline.js +31 -31
  170. umap/static/umap/test/TableEditor.js +29 -25
  171. umap/static/umap/test/_pre.js +2 -7
  172. umap/static/umap/test/index.html +4 -4
  173. umap/storage.py +2 -0
  174. umap/templates/auth/user_form.html +3 -2
  175. umap/templates/base.html +1 -0
  176. umap/templates/registration/login.html +51 -36
  177. umap/templates/umap/about_summary.html +1 -1
  178. umap/templates/umap/branding.html +3 -0
  179. umap/templates/umap/content.html +15 -39
  180. umap/templates/umap/header.html +0 -0
  181. umap/templates/umap/home.html +4 -2
  182. umap/templates/umap/js.html +0 -2
  183. umap/templates/umap/map_detail.html +9 -0
  184. umap/templates/umap/map_init.html +1 -1
  185. umap/templates/umap/map_messages.html +4 -2
  186. umap/templates/umap/map_table.html +130 -69
  187. umap/templates/umap/navigation.html +2 -4
  188. umap/templates/umap/user_dashboard.html +29 -6
  189. umap/tests/base.py +1 -1
  190. umap/tests/integration/conftest.py +18 -0
  191. umap/tests/integration/test_anonymous_owned_map.py +6 -3
  192. umap/tests/integration/test_browser.py +166 -6
  193. umap/tests/integration/test_collaborative_editing.py +23 -5
  194. umap/tests/integration/test_dashboard.py +17 -0
  195. umap/tests/integration/test_edit_datalayer.py +4 -3
  196. umap/tests/integration/test_export_map.py +1 -1
  197. umap/tests/integration/test_import.py +9 -4
  198. umap/tests/integration/test_map.py +64 -0
  199. umap/tests/integration/test_map_preview.py +75 -0
  200. umap/tests/integration/test_owned_map.py +11 -25
  201. umap/tests/integration/test_picto.py +3 -3
  202. umap/tests/integration/test_querystring.py +52 -0
  203. umap/tests/integration/test_share.py +22 -0
  204. umap/tests/test_map_views.py +157 -14
  205. umap/tests/test_views.py +50 -11
  206. umap/urls.py +6 -12
  207. umap/views.py +170 -47
  208. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a1.dist-info}/METADATA +13 -15
  209. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a1.dist-info}/RECORD +212 -200
  210. umap/static/umap/js/umap.browser.js +0 -148
  211. umap/static/umap/js/umap.xhr.js +0 -304
  212. umap/static/umap/test/Controls.js +0 -100
  213. umap/static/umap/test/Map.Init.js +0 -46
  214. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a1.dist-info}/WHEEL +0 -0
  215. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a1.dist-info}/entry_points.txt +0 -0
  216. {umap_project-1.14.0a5.dist-info → umap_project-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,6 @@
1
1
  import json
2
+ import zipfile
3
+ from io import BytesIO
2
4
 
3
5
  import pytest
4
6
  from django.contrib.auth import get_user_model
@@ -8,7 +10,7 @@ from django.urls import reverse
8
10
 
9
11
  from umap.models import DataLayer, Map, Star
10
12
 
11
- from .base import login_required
13
+ from .base import MapFactory, UserFactory, login_required
12
14
 
13
15
  pytestmark = pytest.mark.django_db
14
16
  User = get_user_model()
@@ -19,7 +21,7 @@ def post_data():
19
21
  return {
20
22
  "name": "name",
21
23
  "center": '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
22
- "settings": '{"type":"Feature","geometry":{"type":"Point","coordinates":[5.0592041015625,52.05924589011585]},"properties":{"tilelayer":{"maxZoom":20,"url_template":"http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png","minZoom":0,"attribution":"HOT and friends"},"licence":"","description":"","name":"test enrhûmé","tilelayersControl":true,"displayDataBrowserOnLoad":false,"displayPopupFooter":true,"displayCaptionOnLoad":false,"miniMap":true,"moreControl":true,"scaleControl":true,"zoomControl":true,"datalayersControl":true,"zoom":8}}', # noqa
24
+ "settings": '{"type":"Feature","geometry":{"type":"Point","coordinates":[5.0592041015625,52.05924589011585]},"properties":{"tilelayer":{"maxZoom":20,"url_template":"http://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png","minZoom":0,"attribution":"HOT and friends"},"licence":"","description":"","name":"test enrhûmé","tilelayersControl":true,"displayDataBrowserOnLoad":false,"displayPopupFooter":true,"displayCaptionOnLoad":false,"miniMap":true,"moreControl":true,"scaleControl":true,"zoomControl":true,"datalayersControl":true,"zoom":8}}', # noqa
23
25
  }
24
26
 
25
27
 
@@ -107,7 +109,9 @@ def test_update(client, map, post_data):
107
109
  def test_delete(client, map, datalayer):
108
110
  url = reverse("map_delete", args=(map.pk,))
109
111
  client.login(username=map.owner.username, password="123123")
110
- response = client.post(url, {}, follow=True)
112
+ response = client.post(
113
+ url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
114
+ )
111
115
  assert response.status_code == 200
112
116
  assert not Map.objects.filter(pk=map.pk).exists()
113
117
  assert not DataLayer.objects.filter(pk=datalayer.pk).exists()
@@ -156,9 +160,23 @@ def test_clone_map_should_create_a_new_instance(client, map):
156
160
  url = reverse("map_clone", kwargs={"map_id": map.pk})
157
161
  client.login(username=map.owner.username, password="123123")
158
162
  response = client.post(url)
163
+ assert response.status_code == 302
164
+ assert Map.objects.count() == 2
165
+ clone = Map.objects.latest("pk")
166
+ assert response["Location"] == clone.get_absolute_url()
167
+ assert clone.pk != map.pk
168
+ assert clone.name == "Clone of " + map.name
169
+
170
+
171
+ def test_clone_map_should_be_possible_via_ajax(client, map):
172
+ assert Map.objects.count() == 1
173
+ url = reverse("map_clone", kwargs={"map_id": map.pk})
174
+ client.login(username=map.owner.username, password="123123")
175
+ response = client.post(url, headers={"X-Requested-With": "XMLHttpRequest"})
159
176
  assert response.status_code == 200
160
177
  assert Map.objects.count() == 2
161
178
  clone = Map.objects.latest("pk")
179
+ assert response.json() == {"redirect": clone.get_absolute_url()}
162
180
  assert clone.pk != map.pk
163
181
  assert clone.name == "Clone of " + map.name
164
182
 
@@ -189,7 +207,7 @@ def test_clone_should_set_cloner_as_owner(client, map, user):
189
207
  map.save()
190
208
  client.login(username=user.username, password="123123")
191
209
  response = client.post(url)
192
- assert response.status_code == 200
210
+ assert response.status_code == 302
193
211
  assert Map.objects.count() == 2
194
212
  clone = Map.objects.latest("pk")
195
213
  assert clone.pk != map.pk
@@ -275,7 +293,7 @@ def test_owner_cannot_access_map_with_share_status_blocked(client, map):
275
293
  assert response.status_code == 403
276
294
 
277
295
 
278
- def test_non_editor_cannot_access_map_if_share_status_private(client, map, user): # noqa
296
+ def test_non_editor_cannot_access_map_if_share_status_private(client, map, user):
279
297
  url = reverse("map", args=(map.slug, map.pk))
280
298
  map.share_status = map.PRIVATE
281
299
  map.save()
@@ -296,7 +314,9 @@ def test_only_owner_can_delete(client, map, user):
296
314
  map.editors.add(user)
297
315
  url = reverse("map_delete", kwargs={"map_id": map.pk})
298
316
  client.login(username=user.username, password="123123")
299
- response = client.post(url, {}, follow=True)
317
+ response = client.post(
318
+ url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
319
+ )
300
320
  assert response.status_code == 403
301
321
 
302
322
 
@@ -346,14 +366,14 @@ def test_anonymous_create(cookieclient, post_data):
346
366
 
347
367
 
348
368
  @pytest.mark.usefixtures("allow_anonymous")
349
- def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): # noqa
369
+ def test_anonymous_update_without_cookie_fails(client, anonymap, post_data):
350
370
  url = reverse("map_update", kwargs={"map_id": anonymap.pk})
351
371
  response = client.post(url, post_data)
352
372
  assert response.status_code == 403
353
373
 
354
374
 
355
375
  @pytest.mark.usefixtures("allow_anonymous")
356
- def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data): # noqa
376
+ def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data):
357
377
  url = reverse("map_update", kwargs={"map_id": anonymap.pk})
358
378
  # POST only mendatory fields
359
379
  name = "new map name"
@@ -368,7 +388,9 @@ def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_d
368
388
  @pytest.mark.usefixtures("allow_anonymous")
369
389
  def test_anonymous_delete(cookieclient, anonymap):
370
390
  url = reverse("map_delete", args=(anonymap.pk,))
371
- response = cookieclient.post(url, {}, follow=True)
391
+ response = cookieclient.post(
392
+ url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
393
+ )
372
394
  assert response.status_code == 200
373
395
  assert not Map.objects.filter(pk=anonymap.pk).count()
374
396
  # Test response is a json
@@ -379,7 +401,9 @@ def test_anonymous_delete(cookieclient, anonymap):
379
401
  @pytest.mark.usefixtures("allow_anonymous")
380
402
  def test_no_cookie_cant_delete(client, anonymap):
381
403
  url = reverse("map_delete", args=(anonymap.pk,))
382
- response = client.post(url, {}, follow=True)
404
+ response = client.post(
405
+ url, headers={"X-Requested-With": "XMLHttpRequest"}, follow=True
406
+ )
383
407
  assert response.status_code == 403
384
408
 
385
409
 
@@ -420,7 +444,7 @@ def test_bad_anonymous_edit_url_should_return_403(cookieclient, anonymap):
420
444
  @pytest.mark.usefixtures("allow_anonymous")
421
445
  def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(
422
446
  client, anonymap, user
423
- ): # noqa
447
+ ):
424
448
  assert Map.objects.count() == 1
425
449
  url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
426
450
  anonymap.edit_status = anonymap.OWNER
@@ -434,15 +458,16 @@ def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(
434
458
 
435
459
 
436
460
  @pytest.mark.usefixtures("allow_anonymous")
437
- def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap): # noqa
461
+ def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap):
438
462
  assert Map.objects.count() == 1
439
463
  url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
440
464
  anonymap.edit_status = anonymap.ANONYMOUS
441
465
  anonymap.save()
442
466
  response = client.post(url)
443
- assert response.status_code == 200
467
+ assert response.status_code == 302
444
468
  assert Map.objects.count() == 2
445
469
  clone = Map.objects.latest("pk")
470
+ assert response["Location"] == clone.get_absolute_url()
446
471
  assert clone.pk != anonymap.pk
447
472
  assert clone.name == "Clone of " + anonymap.name
448
473
  assert clone.owner is None
@@ -624,7 +649,7 @@ def test_download(client, map, datalayer):
624
649
  "attribution": "© OSM Contributors",
625
650
  "maxZoom": 18,
626
651
  "minZoom": 0,
627
- "url_template": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png",
652
+ "url_template": "https://a.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png",
628
653
  },
629
654
  "tilelayersControl": True,
630
655
  "zoom": 7,
@@ -656,6 +681,64 @@ def test_download(client, map, datalayer):
656
681
  ]
657
682
 
658
683
 
684
+ def test_download_multiple_maps(client, map, datalayer):
685
+ map.share_status = Map.PRIVATE
686
+ map.save()
687
+ another_map = MapFactory(
688
+ owner=map.owner, name="Another map", share_status=Map.PUBLIC
689
+ )
690
+ client.login(username=map.owner.username, password="123123")
691
+ url = reverse("user_download")
692
+ response = client.get(f"{url}?map_id={map.id}&map_id={another_map.id}")
693
+ assert response.status_code == 200
694
+ with zipfile.ZipFile(file=BytesIO(response.content), mode="r") as f:
695
+ assert len(f.infolist()) == 2
696
+ assert f.infolist()[0].filename == f"umap_backup_test-map_{another_map.id}.umap"
697
+ assert f.infolist()[1].filename == f"umap_backup_test-map_{map.id}.umap"
698
+ with f.open(f.infolist()[1]) as umap_file:
699
+ umapjson = json.loads(umap_file.read().decode())
700
+ assert list(umapjson.keys()) == [
701
+ "type",
702
+ "geometry",
703
+ "properties",
704
+ "uri",
705
+ "layers",
706
+ ]
707
+ assert umapjson["type"] == "umap"
708
+ assert umapjson["uri"] == f"http://testserver/en/map/test-map_{map.id}"
709
+
710
+
711
+ def test_download_multiple_maps_unauthorized(client, map, datalayer):
712
+ map.share_status = Map.PRIVATE
713
+ map.save()
714
+ user1 = UserFactory(username="user1")
715
+ another_map = MapFactory(owner=user1, name="Another map", share_status=Map.PUBLIC)
716
+ client.login(username=map.owner.username, password="123123")
717
+ url = reverse("user_download")
718
+ response = client.get(f"{url}?map_id={map.id}&map_id={another_map.id}")
719
+ assert response.status_code == 200
720
+ with zipfile.ZipFile(file=BytesIO(response.content), mode="r") as f:
721
+ assert len(f.infolist()) == 1
722
+ assert f.infolist()[0].filename == f"umap_backup_test-map_{map.id}.umap"
723
+
724
+
725
+ def test_download_multiple_maps_editor(client, map, datalayer):
726
+ map.share_status = Map.PRIVATE
727
+ map.save()
728
+ user1 = UserFactory(username="user1")
729
+ another_map = MapFactory(owner=user1, name="Another map", share_status=Map.PUBLIC)
730
+ another_map.editors.add(map.owner)
731
+ another_map.save()
732
+ client.login(username=map.owner.username, password="123123")
733
+ url = reverse("user_download")
734
+ response = client.get(f"{url}?map_id={map.id}&map_id={another_map.id}")
735
+ assert response.status_code == 200
736
+ with zipfile.ZipFile(file=BytesIO(response.content), mode="r") as f:
737
+ assert len(f.infolist()) == 2
738
+ assert f.infolist()[0].filename == f"umap_backup_test-map_{another_map.id}.umap"
739
+ assert f.infolist()[1].filename == f"umap_backup_test-map_{map.id}.umap"
740
+
741
+
659
742
  @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.BLOCKED])
660
743
  def test_download_shared_status_map(client, map, datalayer, share_status):
661
744
  map.share_status = share_status
@@ -675,3 +758,63 @@ def test_download_my_map(client, map, datalayer):
675
758
  # Test response is a json
676
759
  j = json.loads(response.content.decode())
677
760
  assert j["type"] == "umap"
761
+
762
+
763
+ @pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.BLOCKED, Map.OPEN])
764
+ def test_oembed_shared_status_map(client, map, datalayer, share_status):
765
+ map.share_status = share_status
766
+ map.save()
767
+ url = f"{reverse('map_oembed')}?url=http://testserver{map.get_absolute_url()}"
768
+ response = client.get(url)
769
+ assert response.status_code == 403
770
+
771
+
772
+ def test_oembed_no_url_map(client, map, datalayer):
773
+ url = reverse("map_oembed")
774
+ response = client.get(url)
775
+ assert response.status_code == 404
776
+
777
+
778
+ def test_oembed_wrong_format_map(client, map, datalayer):
779
+ url = (
780
+ f"{reverse('map_oembed')}"
781
+ f"?url=http://testserver{map.get_absolute_url()}&format=xml"
782
+ )
783
+ response = client.get(url)
784
+ assert response.status_code == 501
785
+
786
+
787
+ def test_oembed_wrong_domain_map(client, map, datalayer):
788
+ url = f"{reverse('map_oembed')}?url=http://BADserver{map.get_absolute_url()}"
789
+ response = client.get(url)
790
+ assert response.status_code == 404
791
+
792
+
793
+ def test_oembed_map(client, map, datalayer):
794
+ url = f"{reverse('map_oembed')}?url=http://testserver{map.get_absolute_url()}"
795
+ response = client.get(url)
796
+ assert response.status_code == 200
797
+ j = json.loads(response.content.decode())
798
+ assert j["type"] == "rich"
799
+ assert j["version"] == "1.0"
800
+ assert j["width"] == 800
801
+ assert j["height"] == 300
802
+ assert j["html"] == (
803
+ '<iframe width="100%" height="300px" frameborder="0" allowfullscreen '
804
+ f'allow="geolocation" src="//testserver/en/map/test-map_{map.id}"></iframe>'
805
+ f'<p><a href="//testserver/en/map/test-map_{map.id}">See full screen</a></p>'
806
+ )
807
+
808
+
809
+ def test_oembed_link(client, map, datalayer):
810
+ response = client.get(map.get_absolute_url())
811
+ assert response.status_code == 200
812
+ assert (
813
+ '<link rel="alternate" type="application/json+oembed"'
814
+ in response.content.decode()
815
+ )
816
+ assert (
817
+ 'href="http://testserver/map/oembed/'
818
+ f'?url=http%3A//testserver/en/map/test-map_{map.id}&format=json"'
819
+ ) in response.content.decode()
820
+ assert 'title="test map oEmbed URL" />' in response.content.decode()
umap/tests/test_views.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import socket
3
- from datetime import date, datetime, timedelta
3
+ from datetime import datetime, timedelta
4
4
 
5
5
  import pytest
6
6
  from django.conf import settings
@@ -10,6 +10,7 @@ from django.urls import reverse
10
10
  from django.utils.timezone import make_aware
11
11
 
12
12
  from umap import VERSION
13
+ from umap.models import Map, Star
13
14
  from umap.views import validate_url
14
15
 
15
16
  from .base import MapFactory, UserFactory
@@ -303,16 +304,6 @@ def test_user_dashboard_display_user_maps_distinct(client, map):
303
304
  assert body.count(anonymap.name) == 0
304
305
 
305
306
 
306
- @pytest.mark.django_db
307
- def test_logout_should_return_json_in_ajax(client, user, settings):
308
- client.login(username=user.username, password="123123")
309
- response = client.get(
310
- reverse("logout"), headers={"X_REQUESTED_WITH": "XMLHttpRequest"}
311
- )
312
- data = json.loads(response.content.decode())
313
- assert data["redirect"] == "/"
314
-
315
-
316
307
  @pytest.mark.django_db
317
308
  def test_logout_should_return_redirect(client, user, settings):
318
309
  client.login(username=user.username, password="123123")
@@ -391,3 +382,51 @@ def test_webmanifest(client):
391
382
  },
392
383
  ]
393
384
  }
385
+
386
+
387
+ @pytest.mark.django_db
388
+ def test_home_feed(client, settings, user, tilelayer):
389
+ settings.UMAP_HOME_FEED = "latest"
390
+ staff = UserFactory(username="Staff", is_staff=True)
391
+ starred = MapFactory(
392
+ owner=user, name="A public map starred by staff", share_status=Map.PUBLIC
393
+ )
394
+ MapFactory(
395
+ owner=user, name="A public map not starred by staff", share_status=Map.PUBLIC
396
+ )
397
+ non_staff = MapFactory(
398
+ owner=user, name="A public map starred by non staff", share_status=Map.PUBLIC
399
+ )
400
+ private = MapFactory(
401
+ owner=user, name="A private map starred by staff", share_status=Map.PRIVATE
402
+ )
403
+ reserved = MapFactory(
404
+ owner=user, name="A reserved map starred by staff", share_status=Map.OPEN
405
+ )
406
+ Star.objects.create(by=staff, map=starred)
407
+ Star.objects.create(by=staff, map=private)
408
+ Star.objects.create(by=staff, map=reserved)
409
+ Star.objects.create(by=user, map=non_staff)
410
+ response = client.get(reverse("home"))
411
+ content = response.content.decode()
412
+ assert "A public map starred by staff" in content
413
+ assert "A public map not starred by staff" in content
414
+ assert "A public map starred by non staff" in content
415
+ assert "A private map starred by staff" not in content
416
+ assert "A reserved map starred by staff" not in content
417
+ settings.UMAP_HOME_FEED = "highlighted"
418
+ response = client.get(reverse("home"))
419
+ content = response.content.decode()
420
+ assert "A public map starred by staff" in content
421
+ assert "A public map not starred by staff" not in content
422
+ assert "A public map starred by non staff" not in content
423
+ assert "A private map starred by staff" not in content
424
+ assert "A reserved map starred by staff" not in content
425
+ settings.UMAP_HOME_FEED = None
426
+ response = client.get(reverse("home"))
427
+ content = response.content.decode()
428
+ assert "A public map starred by staff" not in content
429
+ assert "A public map not starred by staff" not in content
430
+ assert "A public map starred by non staff" not in content
431
+ assert "A private map starred by staff" not in content
432
+ assert "A reserved map starred by staff" not in content
umap/urls.py CHANGED
@@ -15,7 +15,6 @@ from . import views
15
15
  from .decorators import (
16
16
  can_edit_map,
17
17
  can_view_map,
18
- jsonize_view,
19
18
  login_required_if_not_anonymous_allowed,
20
19
  )
21
20
  from .utils import decorated_patterns
@@ -41,6 +40,7 @@ urlpatterns = [
41
40
  ),
42
41
  re_path(r"^i18n/", include("django.conf.urls.i18n")),
43
42
  re_path(r"^agnocomplete/", include("agnocomplete.urls")),
43
+ re_path(r"^map/oembed/", views.MapOEmbed.as_view(), name="map_oembed"),
44
44
  re_path(
45
45
  r"^map/(?P<map_id>\d+)/download/",
46
46
  can_view_map(views.MapDownload.as_view()),
@@ -49,7 +49,7 @@ urlpatterns = [
49
49
  ]
50
50
 
51
51
  i18n_urls = [
52
- re_path(r"^login/$", jsonize_view(auth_views.LoginView.as_view()), name="login"),
52
+ re_path(r"^login/$", auth_views.LoginView.as_view(), name="login"),
53
53
  re_path(
54
54
  r"^login/popup/end/$", views.LoginPopupEnd.as_view(), name="login_popup_end"
55
55
  ),
@@ -96,6 +96,7 @@ i18n_urls += decorated_patterns(
96
96
  )
97
97
  i18n_urls += decorated_patterns(
98
98
  [ensure_csrf_cookie],
99
+ re_path(r"^map/$", views.MapPreview.as_view(), name="map_preview"),
99
100
  re_path(r"^map/new/$", views.MapNew.as_view(), name="map_new"),
100
101
  )
101
102
  i18n_urls += decorated_patterns(
@@ -109,16 +110,9 @@ i18n_urls += decorated_patterns(
109
110
  views.ToggleMapStarStatus.as_view(),
110
111
  name="map_star",
111
112
  ),
112
- re_path(
113
- r"^me$",
114
- views.user_dashboard,
115
- name="user_dashboard",
116
- ),
117
- re_path(
118
- r"^me/profile$",
119
- views.user_profile,
120
- name="user_profile",
121
- ),
113
+ re_path(r"^me$", views.user_dashboard, name="user_dashboard"),
114
+ re_path(r"^me/profile$", views.user_profile, name="user_profile"),
115
+ re_path(r"^me/download$", views.user_download, name="user_download"),
122
116
  )
123
117
  map_urls = [
124
118
  re_path(