umap-project 1.14.0a4__py3-none-any.whl → 2.0.0a0__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 (226) 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 -140
  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/static/umap/vendors/contextmenu/leaflet.contextmenu.css +54 -0
  174. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.js +586 -0
  175. umap/static/umap/vendors/csv2geojson/index.js +259 -0
  176. umap/static/umap/vendors/dompurify/purify.js +1633 -0
  177. umap/static/umap/vendors/locatecontrol/L.Control.Locate.css +63 -0
  178. umap/static/umap/vendors/locatecontrol/L.Control.Locate.js +950 -0
  179. umap/static/umap/vendors/minimap/Control.MiniMap.css +88 -0
  180. umap/static/umap/vendors/minimap/Control.MiniMap.js +352 -0
  181. umap/static/umap/vendors/togeojson/togeojson.js +2 -0
  182. umap/templates/auth/user_form.html +3 -2
  183. umap/templates/base.html +1 -0
  184. umap/templates/registration/login.html +51 -36
  185. umap/templates/umap/about_summary.html +1 -1
  186. umap/templates/umap/branding.html +3 -0
  187. umap/templates/umap/content.html +15 -39
  188. umap/templates/umap/header.html +0 -0
  189. umap/templates/umap/home.html +4 -2
  190. umap/templates/umap/js.html +0 -1
  191. umap/templates/umap/map_detail.html +9 -0
  192. umap/templates/umap/map_init.html +1 -1
  193. umap/templates/umap/map_messages.html +4 -2
  194. umap/templates/umap/map_table.html +130 -69
  195. umap/templates/umap/navigation.html +2 -4
  196. umap/templates/umap/user_dashboard.html +29 -6
  197. umap/tests/base.py +1 -1
  198. umap/tests/integration/conftest.py +18 -0
  199. umap/tests/integration/test_anonymous_owned_map.py +6 -3
  200. umap/tests/integration/test_browser.py +166 -6
  201. umap/tests/integration/test_collaborative_editing.py +142 -0
  202. umap/tests/integration/test_dashboard.py +17 -0
  203. umap/tests/integration/test_edit_datalayer.py +4 -3
  204. umap/tests/integration/test_export_map.py +1 -1
  205. umap/tests/integration/test_import.py +9 -4
  206. umap/tests/integration/test_map.py +64 -0
  207. umap/tests/integration/test_map_preview.py +75 -0
  208. umap/tests/integration/test_owned_map.py +11 -25
  209. umap/tests/integration/test_picto.py +3 -3
  210. umap/tests/integration/test_querystring.py +52 -0
  211. umap/tests/integration/test_share.py +22 -0
  212. umap/tests/test_map_views.py +157 -14
  213. umap/tests/test_merge_features.py +5 -5
  214. umap/tests/test_views.py +50 -11
  215. umap/urls.py +6 -12
  216. umap/utils.py +1 -1
  217. umap/views.py +170 -47
  218. {umap_project-1.14.0a4.dist-info → umap_project-2.0.0a0.dist-info}/METADATA +13 -15
  219. {umap_project-1.14.0a4.dist-info → umap_project-2.0.0a0.dist-info}/RECORD +222 -200
  220. umap/static/umap/js/umap.browser.js +0 -148
  221. umap/static/umap/js/umap.xhr.js +0 -304
  222. umap/static/umap/test/Controls.js +0 -100
  223. umap/static/umap/test/Map.Init.js +0 -46
  224. {umap_project-1.14.0a4.dist-info → umap_project-2.0.0a0.dist-info}/WHEEL +0 -0
  225. {umap_project-1.14.0a4.dist-info → umap_project-2.0.0a0.dist-info}/entry_points.txt +0 -0
  226. {umap_project-1.14.0a4.dist-info → umap_project-2.0.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -10,7 +10,9 @@
10
10
  </div>
11
11
  {% endif %}
12
12
  <div class="wrapper">
13
- <h2 class="section">{% blocktrans %}Get inspired, browse maps{% endblocktrans %}</h2>
14
- <div class="map_list row">{% include "umap/map_list.html" %}</div>
13
+ {% if maps %}
14
+ <h2 class="section">{% blocktrans %}Get inspired, browse maps{% endblocktrans %}</h2>
15
+ <div class="map_list row">{% include "umap/map_list.html" %}</div>
16
+ {% endif %}
15
17
  </div>
16
18
  {% endblock maincontent %}
@@ -36,7 +36,6 @@
36
36
  <script src="{% static 'umap/js/umap.core.js' %}" defer></script>
37
37
  <script src="{% static 'umap/js/umap.autocomplete.js' %}" defer></script>
38
38
  <script src="{% static 'umap/js/umap.popup.js' %}" defer></script>
39
- <script src="{% static 'umap/js/umap.xhr.js' %}" defer></script>
40
39
  <script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
41
40
  <script src="{% static 'umap/js/umap.icon.js' %}" defer></script>
42
41
  <script src="{% static 'umap/js/umap.features.js' %}" defer></script>
@@ -7,9 +7,18 @@
7
7
  map_detail
8
8
  {% endblock body_class %}
9
9
  {% block extra_head %}
10
+ {% if preconnect_domains %}
11
+ {% for domain in preconnect_domains %}
12
+ <link rel="preconnect" href="{{ domain }}" />
13
+ {% endfor %}
14
+ {% endif %}
10
15
  {% umap_css %}
16
+ {{ block.super }}
11
17
  {% umap_js locale=locale %}
12
18
  {% if object.share_status != object.PUBLIC %}<meta name="robots" content="noindex">{% endif %}
19
+ <link rel="alternate" type="application/json+oembed"
20
+ href="{{ oembed_absolute_uri }}?url={{ absolute_uri|urlencode }}&format=json"
21
+ title="{{ map.name }} oEmbed URL" />
13
22
  {% endblock extra_head %}
14
23
  {% block content %}
15
24
  {% block map_init %}
@@ -4,7 +4,7 @@
4
4
  <script defer type="text/javascript">
5
5
  let MAP
6
6
  window.addEventListener('DOMContentLoaded', (event) => {
7
- MAP = new L.U.Map("map", {{ map_settings|notag|safe }});
7
+ MAP = new U.Map("map", {{ map_settings|notag|safe }});
8
8
  });
9
9
  </script>
10
10
  <!-- djlint:on -->
@@ -1,10 +1,12 @@
1
1
  <script type="text/javascript">
2
- {% for m in messages %}
2
+ window.addEventListener('DOMContentLoaded', (event) => {
3
+ {% for m in messages %}
3
4
  {# We have just one, but we need to loop, as for messages API #}
4
- L.U.fire('ui:alert', {
5
+ MAP.ui.alert({
5
6
  content: "{{ m }}",
6
7
  level: "{{ m.tags }}",
7
8
  duration: 100000
8
9
  })
9
10
  {% endfor %}
11
+ })
10
12
  </script>
@@ -1,73 +1,134 @@
1
1
  {% load umap_tags i18n %}
2
- <table class="maps">
3
- <thead>
4
- <tr>
5
- <th>{% blocktrans %}Name{% endblocktrans %}</th>
6
- <th>{% blocktrans %}Preview{% endblocktrans %}</th>
7
- <th>{% blocktrans %}Who can see / edit{% endblocktrans %}</th>
8
- <th>{% blocktrans %}Last save{% endblocktrans %}</th>
9
- <th>{% blocktrans %}Owner{% endblocktrans %}</th>
10
- <th>{% blocktrans %}Actions{% endblocktrans %}</th>
11
- </tr>
12
- </thead>
13
- <tbody>
14
- {% for map_inst in maps %}
15
- {% with unique_id="map_"|addstr:map_inst.pk %}
16
- {{ map_inst.preview_settings|json_script:unique_id }}
17
- <tr>
18
- <td>
19
- <a href="{{ map_inst.get_absolute_url }}">{{ map_inst.name }}</a>
20
- </td>
21
- <td>
22
- <button class="button map-opener neutral" data-map-id="{{ unique_id }}">{% blocktranslate %}Open preview{% endblocktranslate %}</button>
23
- <dialog>
24
- <form method="dialog">
25
- <div id="{{ unique_id }}_target" class="map_fragment"></div>
26
- <p class="close-dialog">
27
- <button class="button" type="submit">Close</button>
28
- </p>
2
+ <div class="table-wrapper">
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th>{% blocktrans %}Name{% endblocktrans %}</th>
7
+ <th>{% blocktrans %}Preview{% endblocktrans %}</th>
8
+ <th>{% blocktrans %}Who can see{% endblocktrans %}</th>
9
+ <th>{% blocktrans %}Who can edit{% endblocktrans %}</th>
10
+ <th>{% blocktrans %}Last save{% endblocktrans %}</th>
11
+ <th>{% blocktrans %}Owner{% endblocktrans %}</th>
12
+ <th>{% blocktrans %}Actions{% endblocktrans %}</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ {% for map_inst in maps %}
17
+ {% with unique_id="map_"|addstr:map_inst.pk %}
18
+ <tr>
19
+ <td>
20
+ <a href="{{ map_inst.get_absolute_url }}">{{ map_inst.name }}</a>
21
+ </td>
22
+ <td>
23
+ {{ map_inst.preview_settings|json_script:unique_id }}
24
+ <button class="map-icon map-opener" data-map-id="{{ unique_id }}"
25
+ title="{% translate "Open preview" %}">
26
+ <span class="icon-dashboard icon-view"></span>
27
+ <span class="sr-only">{% translate "Open preview" %}</span>
28
+ </button>
29
+ <dialog>
30
+ <form method="dialog">
31
+ <div id="{{ unique_id }}_target" class="map_fragment"></div>
32
+ <p class="close-dialog">
33
+ <button class="button" type="submit">Close</button>
34
+ </p>
35
+ </form>
36
+ </dialog>
37
+ </td>
38
+ <td>{{ map_inst.get_share_status_display }}</td>
39
+ <td>{{ map_inst.get_edit_status_display }}</td>
40
+ <td>{{ map_inst.modified_at }}</td>
41
+ <td>
42
+ <a href="{{ map_inst.owner.get_url }}">{{ map_inst.owner }}</a>
43
+ </td>
44
+ <td>
45
+ <a href="{{ map_inst.get_absolute_url }}?share" class="icon-link"
46
+ title="{% translate "Share" %}">
47
+ <span class="icon-dashboard icon-share"></span>
48
+ <span class="sr-only">{% translate "Share" %}</span>
49
+ </a>
50
+ <a href="{{ map_inst.get_absolute_url }}?edit" class="icon-link"
51
+ title="{% translate "Edit" %}">
52
+ <span class="icon-dashboard icon-edit"></span>
53
+ <span class="sr-only">{% translate "Edit" %}</span>
54
+ </a>
55
+ <a href="{% url 'map_download' map_inst.pk %}" class="icon-link"
56
+ title="{% translate "Download" %}">
57
+ <span class="icon-dashboard icon-download"></span>
58
+ <span class="sr-only">{% translate "Download" %}</span>
59
+ </a>
60
+ <form action="{% url 'map_clone' map_inst.pk %}" method="post">
61
+ {% csrf_token %}
62
+ <button class="map-icon" type="submit"
63
+ title="{% translate "Clone" %}">
64
+ <span class="icon-dashboard icon-duplicate"></span>
65
+ <span class="sr-only">{% translate "Clone" %}</span>
66
+ </button>
29
67
  </form>
30
- </dialog>
31
- </td>
32
- <td>{{ map_inst.get_share_status_display }} / {{ map_inst.get_edit_status_display }}</td>
33
- <td>{{ map_inst.modified_at }}</td>
34
- <td>
35
- <a href="{{ map_inst.owner.get_url }}">{{ map_inst.owner }}</a>
36
- </td>
37
- <td>
38
- <a href="{{ map_inst.get_absolute_url }}?share">{% translate "Share" %}</a> |
39
- <a href="{{ map_inst.get_absolute_url }}?edit">{% translate "Edit" %}</a> |
40
- <a href="{% url 'map_download' map_inst.pk %}">{% translate "Download" %}</a>
41
- </td>
42
- </tr>
43
- {% endwith %}
44
- {% endfor %}
45
- </tbody>
46
- </table>
47
- <div class="pagination">
48
- {% if maps.has_previous %}
49
- <a href="?p=1{% if q %}&q={{ q }}{% endif %}">« {% translate "first" %}</a>
50
- <a href="?p={{ maps.previous_page_number }}{% if q %}&q={{ q }}{% endif %}">‹ {% translate "previous" %}</a>
51
- {% else %}
52
- {# djlint:off #}
53
- <span></span>
54
- <span></span>
55
- {# djlint:on #}
56
- {% endif %}
68
+ <form action="{% url 'map_delete' map_inst.pk %}"
69
+ method="post" class="map-delete">
70
+ {% csrf_token %}
71
+ <input type="hidden" name="next" value="{% url 'user_dashboard' %}">
72
+ <button class="map-icon" type="submit"
73
+ title="{% translate "Delete" %}">
74
+ <span class="icon-dashboard icon-delete"></span>
75
+ <span class="sr-only">{% translate "Delete" %}</span>
76
+ </button>
77
+ </form>
78
+ </td>
79
+ </tr>
80
+ {% endwith %}
81
+ {% endfor %}
82
+ </tbody>
83
+ </table>
84
+ </div>
85
+ {% if maps.has_other_pages %}
86
+ <div class="pagination">
87
+ {% if maps.has_previous %}
88
+ <a href="?p=1{% if q %}&q={{ q }}{% endif %}" {% translate "first" %}</a>
89
+ <a href="?p={{ maps.previous_page_number }}{% if q %}&q={{ q }}{% endif %}">‹ {% translate "previous" %}</a>
90
+ {% else %}
91
+ {# djlint:off #}
92
+ <span></span>
93
+ <span></span>
94
+ {# djlint:on #}
95
+ {% endif %}
57
96
 
58
- <span class="current">
59
- {% blocktranslate with maps_number=maps.number num_pages=maps.paginator.num_pages %}
60
- Page {{ maps_number }} of {{ num_pages }}
61
- {% endblocktranslate %}
62
- </span>
97
+ <span class="current">
98
+ {% blocktranslate with maps_number=maps.number num_pages=maps.paginator.num_pages trimmed %}
99
+ Page {{ maps_number }} of {{ num_pages }}
100
+ {% endblocktranslate %}
101
+ </span>
63
102
 
64
- {% if maps.has_next %}
65
- <a href="?p={{ maps.next_page_number }}{% if q %}&q={{ q }}{% endif %}">{% translate "next" %} ›</a>
66
- <a href="?p={{ maps.paginator.num_pages }}{% if q %}&q={{ q }}{% endif %}">{% translate "last" %} »</a>
67
- {% else %}
68
- {# djlint:off #}
69
- <span></span>
70
- <span></span>
71
- {# djlint:on #}
72
- {% endif %}
73
- </div>
103
+ {% if maps.has_next %}
104
+ <a href="?p={{ maps.next_page_number }}{% if q %}&q={{ q }}{% endif %}">{% translate "next" %} ›</a>
105
+ <a href="?p={{ maps.paginator.num_pages }}{% if q %}&q={{ q }}{% endif %}">{% translate "last" %} »</a>
106
+ {% else %}
107
+ {# djlint:off #}
108
+ <span></span>
109
+ <span></span>
110
+ {# djlint:on #}
111
+ {% endif %}
112
+ <span>
113
+ {% blocktranslate with per_page=maps.paginator.per_page trimmed %}
114
+ Lines per page: {{ per_page }}
115
+ {% endblocktranslate %}
116
+ </span>
117
+ <span>
118
+ {% blocktranslate with count=maps.paginator.count trimmed %}
119
+ {{ count }} maps
120
+ {% endblocktranslate %}
121
+ </span>
122
+ </div>
123
+ {% endif %}
124
+ <script>
125
+ !(function () {
126
+ for (const deleteForm of document.querySelectorAll('table.maps form.map-delete')) {
127
+ deleteForm.addEventListener('submit', (event) => {
128
+ if (!confirm(L._('Are you sure you want to delete this map?'))) {
129
+ event.preventDefault()
130
+ }
131
+ })
132
+ }
133
+ })()
134
+ </script>
@@ -1,9 +1,7 @@
1
1
  {% load i18n %}
2
2
  <nav class="umap-nav">
3
3
  <section>
4
- <h1>
5
- <a href="{% url "home" %}">{{ title }}</a>
6
- </h1>
4
+ {% include "umap/branding.html" %}
7
5
  </section>
8
6
  <section>
9
7
  <ul>
@@ -39,7 +37,7 @@
39
37
  </section>
40
38
  <section>
41
39
  {% if not UMAP_READONLY %}
42
- <a href="{% url "map_new" %}" class="button">{% trans "Create a map" %}</a>
40
+ <a href="{% url "map_new" %}" class="button button-primary">{% trans "Create a map" %}</a>
43
41
  {% endif %}
44
42
  </section>
45
43
  </nav>
@@ -5,15 +5,37 @@
5
5
  {% endblock head_title %}
6
6
  {% block maincontent %}
7
7
  {% trans "Search my maps" as placeholder %}
8
- <div class="col wide">
8
+ <div class="row">
9
9
  <h2 class="section tabs">
10
- <a class="selected" href="{% url 'user_dashboard' %}">{% trans "My Dashboard" %}</a> | <a href="{% url 'user_profile' %}">{% trans "My profile" %}</a>
10
+ <a class="selected" href="{% url 'user_dashboard' %}"
11
+ >{% blocktranslate with count=maps.paginator.count %}My Maps ({{ count }}){% endblocktranslate %}
12
+ </a>
13
+ <a href="{% url 'user_profile' %}">{% trans "My profile" %}</a>
11
14
  </h2>
12
- {% include "umap/search_bar.html" with action=request.get_full_path placeholder=placeholder %}
13
15
  </div>
14
16
  <div class="wrapper">
15
17
  <div class="row">
16
- {% if maps %}
18
+ <div class="table-header">
19
+ <form action="{{ request.get_full_path }}" method="get">
20
+ <span>
21
+ <label class="sr-only" for="q">{% translate "Map’s title" %}</label>
22
+ <input id="q" name="q" type="search"
23
+ placeholder="{% translate "Map’s title" %}"
24
+ value="{{ request.GET.q|default:"" }}" />
25
+ </span>
26
+ <input type="submit" value="{% trans "Search my maps" %}" />
27
+ </form>
28
+ {% if maps.object_list|length > 1 %}
29
+ <a href="{% url 'user_download' %}?{% spaceless %}
30
+ {% for map_inst in maps %}map_id={{ map_inst.pk }}{% if not forloop.last %}&{% endif %}{% endfor %}
31
+ {% endspaceless %}" class="button button-download"
32
+ >{% blocktranslate with count=maps.object_list|length trimmed %}
33
+ Download {{ count }} maps
34
+ {% endblocktranslate %}
35
+ </a>
36
+ {% endif %}
37
+ </div>
38
+ {% if maps or request.GET.q %}
17
39
  {% include "umap/map_table.html" %}
18
40
  {% else %}
19
41
  <div>
@@ -31,8 +53,9 @@
31
53
  const CACHE = {}
32
54
  for (const mapOpener of document.querySelectorAll("button.map-opener")) {
33
55
  mapOpener.addEventListener('click', (event) => {
34
- event.target.nextElementSibling.showModal()
35
- const mapId = event.target.dataset.mapId
56
+ const button = event.target.closest('button')
57
+ button.nextElementSibling.showModal()
58
+ const mapId = button.dataset.mapId
36
59
  if (!document.querySelector(`#${mapId}_target`).hasChildNodes()) {
37
60
  const previewSettings = JSON.parse(document.getElementById(mapId).textContent)
38
61
  const map = new L.U.Map(`${mapId}_target`, previewSettings)
umap/tests/base.py CHANGED
@@ -81,7 +81,7 @@ class MapFactory(factory.django.DjangoModelFactory):
81
81
  "attribution": "\xa9 OSM Contributors",
82
82
  "maxZoom": 18,
83
83
  "minZoom": 0,
84
- "url_template": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png",
84
+ "url_template": "https://a.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png",
85
85
  },
86
86
  "tilelayersControl": True,
87
87
  "zoom": 7,
@@ -0,0 +1,18 @@
1
+ import pytest
2
+
3
+
4
+ @pytest.fixture
5
+ def login(context, settings, live_server):
6
+ def do_login(user):
7
+ # TODO use storage state to do login only once per session
8
+ # https://playwright.dev/python/docs/auth
9
+ settings.ENABLE_ACCOUNT_LOGIN = True
10
+ page = context.new_page()
11
+ page.goto(f"{live_server.url}/en/")
12
+ page.locator(".login").click()
13
+ page.get_by_placeholder("Username").fill(user.username)
14
+ page.get_by_placeholder("Password").fill("123123")
15
+ page.locator('#login_form input[type="submit"]').click()
16
+ return page
17
+
18
+ return do_login
@@ -1,5 +1,4 @@
1
1
  import re
2
- from time import sleep
3
2
 
4
3
  import pytest
5
4
  from django.core.signing import get_cookie_signer
@@ -126,10 +125,14 @@ def test_anonymous_can_add_marker_on_editable_layer(
126
125
 
127
126
  def test_can_change_perms_after_create(tilelayer, live_server, page):
128
127
  page.goto(f"{live_server.url}/en/map/new")
128
+ # Create a layer
129
+ page.get_by_title("Manage layers").click()
130
+ page.get_by_role("button", name="Add a layer").click()
131
+ page.locator("input[name=name]").fill("Layer 1")
129
132
  save = page.get_by_role("button", name="Save")
130
133
  expect(save).to_be_visible()
131
- save.click()
132
- sleep(1) # Let save ajax go back
134
+ with page.expect_response(re.compile(r".*/datalayer/create/.*")):
135
+ save.click()
133
136
  edit_permissions = page.get_by_title("Update permissions and editors")
134
137
  expect(edit_permissions).to_be_visible()
135
138
  edit_permissions.click()
@@ -1,8 +1,11 @@
1
+ from copy import deepcopy
1
2
  from time import sleep
2
3
 
3
4
  import pytest
4
5
  from playwright.sync_api import expect
5
6
 
7
+ from umap.models import Map
8
+
6
9
  from ..base import DataLayerFactory
7
10
 
8
11
  pytestmark = pytest.mark.django_db
@@ -13,12 +16,12 @@ DATALAYER_DATA = {
13
16
  "features": [
14
17
  {
15
18
  "type": "Feature",
16
- "properties": {"name": "one point in france"},
19
+ "properties": {"name": "one point in france", "foo": "point"},
17
20
  "geometry": {"type": "Point", "coordinates": [3.339844, 46.920255]},
18
21
  },
19
22
  {
20
23
  "type": "Feature",
21
- "properties": {"name": "one polygon in greenland"},
24
+ "properties": {"name": "one polygon in greenland", "foo": "polygon"},
22
25
  "geometry": {
23
26
  "type": "Polygon",
24
27
  "coordinates": [
@@ -34,7 +37,7 @@ DATALAYER_DATA = {
34
37
  },
35
38
  {
36
39
  "type": "Feature",
37
- "properties": {"name": "one line in new zeland"},
40
+ "properties": {"name": "one line in new zeland", "foo": "line"},
38
41
  "geometry": {
39
42
  "type": "LineString",
40
43
  "coordinates": [
@@ -71,15 +74,32 @@ def test_data_browser_should_be_open(live_server, page, bootstrap, map):
71
74
 
72
75
  def test_data_browser_should_be_filterable(live_server, page, bootstrap, map):
73
76
  page.goto(f"{live_server.url}{map.get_absolute_url()}")
77
+ expect(page.get_by_title("Features in this layer: 3")).to_be_visible()
74
78
  markers = page.locator(".leaflet-marker-icon")
79
+ paths = page.locator(".leaflet-overlay-pane path")
75
80
  expect(markers).to_have_count(1)
76
- el = page.locator("input[name='filter']")
77
- expect(el).to_be_visible()
78
- el.type("poly")
81
+ expect(paths).to_have_count(2)
82
+ filter_ = page.locator("input[name='filter']")
83
+ expect(filter_).to_be_visible()
84
+ filter_.type("poly")
85
+ expect(page.get_by_title("Features in this layer: 1/3")).to_be_visible()
86
+ expect(page.get_by_title("Features in this layer: 1/3")).to_have_text("1/3")
79
87
  expect(page.get_by_text("one point in france")).to_be_hidden()
80
88
  expect(page.get_by_text("one line in new zeland")).to_be_hidden()
81
89
  expect(page.get_by_text("one polygon in greenland")).to_be_visible()
82
90
  expect(markers).to_have_count(0) # Hidden by filter
91
+ expect(paths).to_have_count(1) # Only polygon
92
+ # Empty the filter
93
+ filter_.fill("")
94
+ filter_.blur()
95
+ expect(markers).to_have_count(1)
96
+ expect(paths).to_have_count(2)
97
+ filter_.type("point")
98
+ expect(page.get_by_text("one point in france")).to_be_visible()
99
+ expect(page.get_by_text("one line in new zeland")).to_be_hidden()
100
+ expect(page.get_by_text("one polygon in greenland")).to_be_hidden()
101
+ expect(markers).to_have_count(1)
102
+ expect(paths).to_have_count(0)
83
103
 
84
104
 
85
105
  def test_data_browser_can_show_only_visible_features(live_server, page, bootstrap, map):
@@ -131,3 +151,143 @@ def test_data_browser_bbox_limit_should_be_dynamic(live_server, page, bootstrap,
131
151
  expect(page.get_by_text("one point in france")).to_be_visible()
132
152
  expect(page.get_by_text("one polygon in greenland")).to_be_visible()
133
153
  expect(page.get_by_text("one line in new zeland")).to_be_hidden()
154
+
155
+
156
+ def test_data_browser_bbox_filter_should_be_persistent(
157
+ live_server, page, bootstrap, map
158
+ ):
159
+ # Zoom on Europe
160
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/51.000/2.000")
161
+ el = page.get_by_text("Current map view")
162
+ expect(el).to_be_visible()
163
+ el.click()
164
+ browser = page.locator("#umap-ui-container")
165
+ expect(browser.get_by_text("one point in france")).to_be_visible()
166
+ expect(browser.get_by_text("one line in new zeland")).to_be_hidden()
167
+ expect(browser.get_by_text("one polygon in greenland")).to_be_hidden()
168
+ # Close and reopen the browser to make sure this settings is persistent
169
+ close = browser.get_by_text("Close")
170
+ close.click()
171
+ expect(browser).to_be_hidden()
172
+ sleep(0.5)
173
+ expect(browser.get_by_text("one point in france")).to_be_hidden()
174
+ expect(browser.get_by_text("one line in new zeland")).to_be_hidden()
175
+ expect(browser.get_by_text("one polygon in greenland")).to_be_hidden()
176
+ page.get_by_title("See data layers").click()
177
+ page.get_by_role("button", name="Browse data").click()
178
+ expect(browser.get_by_text("one point in france")).to_be_visible()
179
+ expect(browser.get_by_text("one line in new zeland")).to_be_hidden()
180
+ expect(browser.get_by_text("one polygon in greenland")).to_be_hidden()
181
+
182
+
183
+ def test_data_browser_bbox_filtered_is_clickable(live_server, page, bootstrap, map):
184
+ popup = page.locator(".leaflet-popup")
185
+ # Zoom on Europe
186
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/51.000/2.000")
187
+ el = page.get_by_text("Current map view")
188
+ expect(el).to_be_visible()
189
+ el.click()
190
+ browser = page.locator("#umap-ui-container")
191
+ expect(browser.get_by_text("one point in france")).to_be_visible()
192
+ expect(browser.get_by_text("one line in new zeland")).to_be_hidden()
193
+ expect(browser.get_by_text("one polygon in greenland")).to_be_hidden()
194
+ expect(popup).to_be_hidden()
195
+ browser.get_by_text("one point in france").click()
196
+ expect(popup).to_be_visible()
197
+ expect(popup.get_by_text("one point in france")).to_be_visible()
198
+
199
+
200
+ def test_data_browser_with_variable_in_name(live_server, page, bootstrap, map):
201
+ # Include a variable
202
+ map.settings["properties"]["labelKey"] = "{name} ({foo})"
203
+ map.save()
204
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
205
+ expect(page.get_by_text("one point in france (point)")).to_be_visible()
206
+ expect(page.get_by_text("one line in new zeland (line)")).to_be_visible()
207
+ expect(page.get_by_text("one polygon in greenland (polygon)")).to_be_visible()
208
+ filter_ = page.locator("input[name='filter']")
209
+ expect(filter_).to_be_visible()
210
+ filter_.type("foobar") # Hide all
211
+ expect(page.get_by_text("one point in france (point)")).to_be_hidden()
212
+ expect(page.get_by_text("one line in new zeland (line)")).to_be_hidden()
213
+ expect(page.get_by_text("one polygon in greenland (polygon)")).to_be_hidden()
214
+ # Empty back the filter
215
+ filter_.fill("")
216
+ filter_.blur()
217
+ expect(page.get_by_text("one point in france (point)")).to_be_visible()
218
+ expect(page.get_by_text("one line in new zeland (line)")).to_be_visible()
219
+ expect(page.get_by_text("one polygon in greenland (polygon)")).to_be_visible()
220
+
221
+
222
+ def test_can_open_databrowser_from_layers_list(live_server, map, page, bootstrap):
223
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
224
+ page.get_by_title("See data layers").click()
225
+ page.get_by_role("button", name="Browse data").click()
226
+ browser = page.locator(".umap-browse-data")
227
+ expect(browser).to_be_visible()
228
+ expect(browser.get_by_text("test datalayer")).to_be_visible()
229
+ expect(browser.get_by_text("one point in france")).to_be_visible()
230
+ expect(browser.get_by_text("one line in new zeland")).to_be_visible()
231
+ expect(browser.get_by_text("one polygon in greenland")).to_be_visible()
232
+
233
+
234
+ def test_should_sort_features_in_natural_order(live_server, map, page):
235
+ map.settings["properties"]["onLoadPanel"] = "databrowser"
236
+ map.save()
237
+ datalayer_data = deepcopy(DATALAYER_DATA)
238
+ datalayer_data["features"][0]["properties"]["name"] = "9. a marker"
239
+ datalayer_data["features"][1]["properties"]["name"] = "1. a poly"
240
+ datalayer_data["features"][2]["properties"]["name"] = "100. a line"
241
+ DataLayerFactory(map=map, data=datalayer_data)
242
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
243
+ features = page.locator(".umap-browse-data li")
244
+ expect(features).to_have_count(3)
245
+ expect(features.nth(0)).to_have_text("1. a poly")
246
+ expect(features.nth(1)).to_have_text("9. a marker")
247
+ expect(features.nth(2)).to_have_text("100. a line")
248
+
249
+
250
+ def test_should_redraw_list_on_feature_delete(live_server, map, page, bootstrap):
251
+ map.edit_status = Map.ANONYMOUS
252
+ map.save()
253
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
254
+ # Enable edit
255
+ page.get_by_role("button", name="Edit").click()
256
+ buttons = page.locator(".umap-browse-data li .feature-delete")
257
+ expect(buttons).to_have_count(3)
258
+ page.on("dialog", lambda dialog: dialog.accept())
259
+ buttons.nth(0).click()
260
+ expect(buttons).to_have_count(2)
261
+ page.get_by_role("button", name="Cancel edits").click()
262
+ expect(buttons).to_have_count(3)
263
+
264
+
265
+ def test_should_show_header_for_display_on_load_false(
266
+ live_server, page, bootstrap, map, datalayer
267
+ ):
268
+ datalayer.settings["displayOnLoad"] = False
269
+ datalayer.settings["name"] = "This layer is not loaded"
270
+ datalayer.save()
271
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
272
+ browser = page.locator(".umap-browse-data")
273
+ expect(browser).to_be_visible()
274
+ expect(browser.get_by_text("This layer is not loaded")).to_be_visible()
275
+
276
+
277
+ def test_should_use_color_variable(live_server, map, page):
278
+ map.settings["properties"]["onLoadPanel"] = "databrowser"
279
+ map.settings["properties"]["color"] = "{mycolor}"
280
+ map.save()
281
+ datalayer_data = deepcopy(DATALAYER_DATA)
282
+ datalayer_data["features"][0]["properties"]["mycolor"] = "DarkRed"
283
+ datalayer_data["features"][2]["properties"]["mycolor"] = "DarkGreen"
284
+ DataLayerFactory(map=map, data=datalayer_data)
285
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
286
+ features = page.locator(".umap-browse-data li .feature-color")
287
+ expect(features).to_have_count(3)
288
+ # DarkGreen
289
+ expect(features.nth(0)).to_have_css("background-color", "rgb(0, 100, 0)")
290
+ # DarkRed
291
+ expect(features.nth(1)).to_have_css("background-color", "rgb(139, 0, 0)")
292
+ # DarkBlue (default color)
293
+ expect(features.nth(2)).to_have_css("background-color", "rgb(0, 0, 139)")