umap-project 2.7.3__py3-none-any.whl → 2.8.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.
Files changed (279) hide show
  1. umap/__init__.py +1 -1
  2. umap/forms.py +4 -14
  3. umap/locale/am_ET/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/am_ET/LC_MESSAGES/django.po +278 -151
  5. umap/locale/ar/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/ar/LC_MESSAGES/django.po +335 -141
  7. umap/locale/bg/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/bg/LC_MESSAGES/django.po +279 -152
  9. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/br/LC_MESSAGES/django.po +95 -79
  11. umap/locale/ca/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/ca/LC_MESSAGES/django.po +85 -68
  13. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/cs_CZ/LC_MESSAGES/django.po +78 -66
  15. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/da/LC_MESSAGES/django.po +280 -153
  17. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/de/LC_MESSAGES/django.po +80 -64
  19. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/el/LC_MESSAGES/django.po +82 -66
  21. umap/locale/en/LC_MESSAGES/django.po +73 -61
  22. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/es/LC_MESSAGES/django.po +75 -63
  24. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/et/LC_MESSAGES/django.po +280 -153
  26. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/eu/LC_MESSAGES/django.po +82 -66
  28. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/fa_IR/LC_MESSAGES/django.po +80 -64
  30. umap/locale/fi/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/fi/LC_MESSAGES/django.po +278 -151
  32. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/fr/LC_MESSAGES/django.po +75 -63
  34. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/gl/LC_MESSAGES/django.po +280 -153
  36. umap/locale/he/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/he/LC_MESSAGES/django.po +281 -154
  38. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/hu/LC_MESSAGES/django.po +80 -64
  40. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  41. umap/locale/is/LC_MESSAGES/django.po +280 -153
  42. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  43. umap/locale/it/LC_MESSAGES/django.po +82 -66
  44. umap/locale/ja/LC_MESSAGES/django.mo +0 -0
  45. umap/locale/ja/LC_MESSAGES/django.po +280 -153
  46. umap/locale/ko/LC_MESSAGES/django.mo +0 -0
  47. umap/locale/ko/LC_MESSAGES/django.po +280 -153
  48. umap/locale/lt/LC_MESSAGES/django.mo +0 -0
  49. umap/locale/lt/LC_MESSAGES/django.po +280 -153
  50. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  51. umap/locale/ms/LC_MESSAGES/django.po +82 -66
  52. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  53. umap/locale/nl/LC_MESSAGES/django.po +280 -153
  54. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  55. umap/locale/pl/LC_MESSAGES/django.po +82 -66
  56. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  57. umap/locale/pt/LC_MESSAGES/django.po +75 -63
  58. umap/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  59. umap/locale/pt_BR/LC_MESSAGES/django.po +280 -153
  60. umap/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  61. umap/locale/pt_PT/LC_MESSAGES/django.po +280 -153
  62. umap/locale/ru/LC_MESSAGES/django.mo +0 -0
  63. umap/locale/ru/LC_MESSAGES/django.po +280 -153
  64. umap/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  65. umap/locale/sk_SK/LC_MESSAGES/django.po +280 -153
  66. umap/locale/sl/LC_MESSAGES/django.mo +0 -0
  67. umap/locale/sl/LC_MESSAGES/django.po +280 -153
  68. umap/locale/sr/LC_MESSAGES/django.mo +0 -0
  69. umap/locale/sr/LC_MESSAGES/django.po +280 -153
  70. umap/locale/sv/LC_MESSAGES/django.mo +0 -0
  71. umap/locale/sv/LC_MESSAGES/django.po +81 -65
  72. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  73. umap/locale/th_TH/LC_MESSAGES/django.po +257 -185
  74. umap/locale/tr/LC_MESSAGES/django.mo +0 -0
  75. umap/locale/tr/LC_MESSAGES/django.po +280 -153
  76. umap/locale/uk_UA/LC_MESSAGES/django.mo +0 -0
  77. umap/locale/uk_UA/LC_MESSAGES/django.po +280 -153
  78. umap/locale/vi/LC_MESSAGES/django.mo +0 -0
  79. umap/locale/vi/LC_MESSAGES/django.po +278 -151
  80. umap/locale/zh/LC_MESSAGES/django.mo +0 -0
  81. umap/locale/zh/LC_MESSAGES/django.po +278 -151
  82. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  83. umap/locale/zh_TW/LC_MESSAGES/django.po +97 -81
  84. umap/management/commands/empty_trash.py +32 -0
  85. umap/management/commands/migrate_to_S3.py +29 -0
  86. umap/migrations/0023_alter_datalayer_uuid.py +19 -0
  87. umap/migrations/0024_alter_map_share_status.py +30 -0
  88. umap/migrations/0025_alter_datalayer_geojson.py +24 -0
  89. umap/models.py +68 -116
  90. umap/settings/base.py +22 -2
  91. umap/static/umap/base.css +3 -603
  92. umap/static/umap/content.css +5 -3
  93. umap/static/umap/css/bar.css +202 -0
  94. umap/static/umap/css/form.css +617 -0
  95. umap/static/umap/css/icon.css +21 -1
  96. umap/static/umap/css/popup.css +125 -0
  97. umap/static/umap/img/16-white.svg +16 -4
  98. umap/static/umap/img/16.svg +1 -1
  99. umap/static/umap/img/source/16-white.svg +46 -45
  100. umap/static/umap/img/source/16.svg +1 -753
  101. umap/static/umap/js/components/fragment.js +3 -1
  102. umap/static/umap/js/modules/browser.js +20 -19
  103. umap/static/umap/js/modules/caption.js +21 -22
  104. umap/static/umap/js/modules/data/features.js +101 -74
  105. umap/static/umap/js/modules/data/layer.js +157 -137
  106. umap/static/umap/js/modules/facets.js +9 -9
  107. umap/static/umap/js/modules/formatter.js +5 -5
  108. umap/static/umap/js/modules/global.js +4 -52
  109. umap/static/umap/js/modules/help.js +18 -21
  110. umap/static/umap/js/modules/importer.js +71 -39
  111. umap/static/umap/js/modules/importers/cadastrefr.js +4 -0
  112. umap/static/umap/js/modules/importers/geodatamine.js +3 -3
  113. umap/static/umap/js/modules/importers/overpass.js +5 -0
  114. umap/static/umap/js/modules/permissions.js +85 -87
  115. umap/static/umap/js/modules/rendering/layers/base.js +15 -15
  116. umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
  117. umap/static/umap/js/modules/rendering/layers/cluster.js +1 -1
  118. umap/static/umap/js/modules/rendering/layers/heat.js +1 -1
  119. umap/static/umap/js/modules/rendering/map.js +390 -0
  120. umap/static/umap/js/modules/rendering/popup.js +10 -9
  121. umap/static/umap/js/modules/rendering/template.js +35 -12
  122. umap/static/umap/js/modules/rendering/ui.js +57 -12
  123. umap/static/umap/js/modules/rules.js +22 -25
  124. umap/static/umap/js/modules/saving.js +47 -0
  125. umap/static/umap/js/modules/schema.js +5 -0
  126. umap/static/umap/js/modules/share.js +21 -24
  127. umap/static/umap/js/modules/slideshow.js +24 -20
  128. umap/static/umap/js/modules/sync/updaters.js +7 -9
  129. umap/static/umap/js/modules/tableeditor.js +20 -19
  130. umap/static/umap/js/modules/ui/bar.js +196 -0
  131. umap/static/umap/js/modules/ui/panel.js +10 -9
  132. umap/static/umap/js/modules/umap.js +1668 -0
  133. umap/static/umap/js/modules/urls.js +2 -2
  134. umap/static/umap/js/modules/utils.js +20 -6
  135. umap/static/umap/js/umap.controls.js +74 -301
  136. umap/static/umap/js/umap.core.js +29 -50
  137. umap/static/umap/js/umap.forms.js +34 -27
  138. umap/static/umap/keycloak.png +0 -0
  139. umap/static/umap/locale/am_ET.js +26 -10
  140. umap/static/umap/locale/am_ET.json +26 -10
  141. umap/static/umap/locale/ar.js +26 -10
  142. umap/static/umap/locale/ar.json +26 -10
  143. umap/static/umap/locale/ast.js +26 -10
  144. umap/static/umap/locale/ast.json +26 -10
  145. umap/static/umap/locale/bg.js +26 -10
  146. umap/static/umap/locale/bg.json +26 -10
  147. umap/static/umap/locale/br.js +27 -20
  148. umap/static/umap/locale/br.json +27 -20
  149. umap/static/umap/locale/ca.js +32 -29
  150. umap/static/umap/locale/ca.json +32 -29
  151. umap/static/umap/locale/cs_CZ.js +24 -17
  152. umap/static/umap/locale/cs_CZ.json +24 -17
  153. umap/static/umap/locale/da.js +26 -10
  154. umap/static/umap/locale/da.json +26 -10
  155. umap/static/umap/locale/de.js +21 -14
  156. umap/static/umap/locale/de.json +21 -14
  157. umap/static/umap/locale/el.js +28 -12
  158. umap/static/umap/locale/el.json +28 -12
  159. umap/static/umap/locale/en.js +12 -9
  160. umap/static/umap/locale/en.json +12 -9
  161. umap/static/umap/locale/en_US.json +26 -10
  162. umap/static/umap/locale/es.js +16 -13
  163. umap/static/umap/locale/es.json +16 -13
  164. umap/static/umap/locale/et.js +26 -10
  165. umap/static/umap/locale/et.json +26 -10
  166. umap/static/umap/locale/eu.js +16 -9
  167. umap/static/umap/locale/eu.json +16 -9
  168. umap/static/umap/locale/fa_IR.js +16 -9
  169. umap/static/umap/locale/fa_IR.json +16 -9
  170. umap/static/umap/locale/fi.js +26 -10
  171. umap/static/umap/locale/fi.json +26 -10
  172. umap/static/umap/locale/fr.js +12 -9
  173. umap/static/umap/locale/fr.json +12 -9
  174. umap/static/umap/locale/gl.js +26 -10
  175. umap/static/umap/locale/gl.json +26 -10
  176. umap/static/umap/locale/he.js +26 -10
  177. umap/static/umap/locale/he.json +26 -10
  178. umap/static/umap/locale/hr.js +26 -10
  179. umap/static/umap/locale/hr.json +26 -10
  180. umap/static/umap/locale/hu.js +16 -9
  181. umap/static/umap/locale/hu.json +16 -9
  182. umap/static/umap/locale/id.js +26 -10
  183. umap/static/umap/locale/id.json +26 -10
  184. umap/static/umap/locale/is.js +26 -10
  185. umap/static/umap/locale/is.json +26 -10
  186. umap/static/umap/locale/it.js +26 -10
  187. umap/static/umap/locale/it.json +26 -10
  188. umap/static/umap/locale/ja.js +26 -10
  189. umap/static/umap/locale/ja.json +26 -10
  190. umap/static/umap/locale/ko.js +26 -10
  191. umap/static/umap/locale/ko.json +26 -10
  192. umap/static/umap/locale/lt.js +26 -10
  193. umap/static/umap/locale/lt.json +26 -10
  194. umap/static/umap/locale/ms.js +28 -12
  195. umap/static/umap/locale/ms.json +28 -12
  196. umap/static/umap/locale/nl.js +28 -12
  197. umap/static/umap/locale/nl.json +28 -12
  198. umap/static/umap/locale/no.js +26 -10
  199. umap/static/umap/locale/no.json +26 -10
  200. umap/static/umap/locale/pl.js +28 -12
  201. umap/static/umap/locale/pl.json +28 -12
  202. umap/static/umap/locale/pl_PL.json +26 -10
  203. umap/static/umap/locale/pt.js +16 -9
  204. umap/static/umap/locale/pt.json +16 -9
  205. umap/static/umap/locale/pt_BR.js +26 -10
  206. umap/static/umap/locale/pt_BR.json +26 -10
  207. umap/static/umap/locale/pt_PT.js +16 -9
  208. umap/static/umap/locale/pt_PT.json +16 -9
  209. umap/static/umap/locale/ro.js +26 -10
  210. umap/static/umap/locale/ro.json +26 -10
  211. umap/static/umap/locale/ru.js +26 -10
  212. umap/static/umap/locale/ru.json +26 -10
  213. umap/static/umap/locale/si.js +7 -7
  214. umap/static/umap/locale/si.json +7 -7
  215. umap/static/umap/locale/sk_SK.js +26 -10
  216. umap/static/umap/locale/sk_SK.json +26 -10
  217. umap/static/umap/locale/sl.js +26 -10
  218. umap/static/umap/locale/sl.json +26 -10
  219. umap/static/umap/locale/sr.js +26 -10
  220. umap/static/umap/locale/sr.json +26 -10
  221. umap/static/umap/locale/sv.js +27 -11
  222. umap/static/umap/locale/sv.json +27 -11
  223. umap/static/umap/locale/th_TH.js +28 -12
  224. umap/static/umap/locale/th_TH.json +28 -12
  225. umap/static/umap/locale/tr.js +26 -10
  226. umap/static/umap/locale/tr.json +26 -10
  227. umap/static/umap/locale/uk_UA.js +26 -10
  228. umap/static/umap/locale/uk_UA.json +26 -10
  229. umap/static/umap/locale/vi.js +26 -10
  230. umap/static/umap/locale/vi.json +26 -10
  231. umap/static/umap/locale/vi_VN.json +26 -10
  232. umap/static/umap/locale/zh.js +26 -10
  233. umap/static/umap/locale/zh.json +26 -10
  234. umap/static/umap/locale/zh_CN.json +26 -10
  235. umap/static/umap/locale/zh_TW.Big5.json +26 -10
  236. umap/static/umap/locale/zh_TW.js +34 -27
  237. umap/static/umap/locale/zh_TW.json +34 -27
  238. umap/static/umap/map.css +5 -364
  239. umap/static/umap/unittests/URLs.js +15 -15
  240. umap/static/umap/unittests/utils.js +23 -1
  241. umap/static/umap/vars.css +2 -0
  242. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +5 -1
  243. umap/storage.py +152 -0
  244. umap/templates/registration/login.html +7 -6
  245. umap/templates/umap/css.html +3 -0
  246. umap/templates/umap/js.html +1 -2
  247. umap/templates/umap/map_init.html +4 -5
  248. umap/templates/umap/user_dashboard.html +18 -19
  249. umap/tests/base.py +5 -1
  250. umap/tests/integration/conftest.py +2 -1
  251. umap/tests/integration/test_anonymous_owned_map.py +18 -10
  252. umap/tests/integration/test_browser.py +16 -1
  253. umap/tests/integration/test_dashboard.py +1 -1
  254. umap/tests/integration/test_edit_datalayer.py +18 -7
  255. umap/tests/integration/test_import.py +8 -5
  256. umap/tests/integration/test_optimistic_merge.py +31 -8
  257. umap/tests/integration/test_owned_map.py +22 -16
  258. umap/tests/integration/test_popup.py +44 -0
  259. umap/tests/integration/test_save.py +35 -0
  260. umap/tests/integration/test_view_marker.py +12 -0
  261. umap/tests/integration/test_view_polyline.py +257 -0
  262. umap/tests/integration/test_websocket_sync.py +81 -9
  263. umap/tests/test_datalayer.py +6 -7
  264. umap/tests/test_datalayer_s3.py +135 -0
  265. umap/tests/test_datalayer_views.py +28 -10
  266. umap/tests/test_empty_trash.py +34 -0
  267. umap/tests/test_map.py +12 -3
  268. umap/tests/test_map_views.py +69 -37
  269. umap/tests/test_views.py +53 -0
  270. umap/urls.py +3 -3
  271. umap/views.py +107 -76
  272. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/METADATA +16 -13
  273. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/RECORD +276 -262
  274. umap/management/commands/purge_purgatory.py +0 -28
  275. umap/static/umap/js/umap.js +0 -1903
  276. umap/tests/test_purge_purgatory.py +0 -25
  277. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/WHEEL +0 -0
  278. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/entry_points.txt +0 -0
  279. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/licenses/LICENSE +0 -0
umap/models.py CHANGED
@@ -1,17 +1,12 @@
1
1
  import json
2
- import operator
3
- import os
4
- import shutil
5
- import time
6
2
  import uuid
7
- from pathlib import Path
8
3
 
9
4
  from django.conf import settings
10
5
  from django.contrib.auth.models import User
11
6
  from django.contrib.gis.db import models
12
7
  from django.core.files.base import File
8
+ from django.core.files.storage import storages
13
9
  from django.core.signing import Signer
14
- from django.template.defaultfilters import slugify
15
10
  from django.urls import reverse
16
11
  from django.utils.functional import classproperty
17
12
  from django.utils.translation import gettext_lazy as _
@@ -38,13 +33,22 @@ def get_user_stars_url(self):
38
33
  return reverse("user_stars", kwargs={"identifier": identifier})
39
34
 
40
35
 
36
+ def get_user_metadata(self):
37
+ return {
38
+ "id": self.pk,
39
+ "name": str(self),
40
+ "url": self.get_url(),
41
+ }
42
+
43
+
41
44
  User.add_to_class("__str__", display_name)
42
45
  User.add_to_class("get_url", get_user_url)
43
46
  User.add_to_class("get_stars_url", get_user_stars_url)
47
+ User.add_to_class("get_metadata", get_user_metadata)
44
48
 
45
49
 
46
50
  def get_default_share_status():
47
- return settings.UMAP_DEFAULT_SHARE_STATUS or Map.PUBLIC
51
+ return settings.UMAP_DEFAULT_SHARE_STATUS or Map.DRAFT
48
52
 
49
53
 
50
54
  def get_default_edit_status():
@@ -161,20 +165,30 @@ class Map(NamedModel):
161
165
  ANONYMOUS = 1
162
166
  COLLABORATORS = 2
163
167
  OWNER = 3
168
+ DRAFT = 0
164
169
  PUBLIC = 1
165
170
  OPEN = 2
166
171
  PRIVATE = 3
167
172
  BLOCKED = 9
173
+ DELETED = 99
174
+ ANONYMOUS_EDIT_STATUS = (
175
+ (OWNER, _("Only editable with secret edit link")),
176
+ (ANONYMOUS, _("Everyone can edit")),
177
+ )
168
178
  EDIT_STATUS = (
169
179
  (ANONYMOUS, _("Everyone")),
170
180
  (COLLABORATORS, _("Editors and team only")),
171
181
  (OWNER, _("Owner only")),
172
182
  )
173
- SHARE_STATUS = (
183
+ ANONYMOUS_SHARE_STATUS = (
184
+ (DRAFT, _("Draft (private)")),
174
185
  (PUBLIC, _("Everyone (public)")),
186
+ )
187
+ SHARE_STATUS = ANONYMOUS_SHARE_STATUS + (
175
188
  (OPEN, _("Anyone with link")),
176
189
  (PRIVATE, _("Editors and team only")),
177
190
  (BLOCKED, _("Blocked")),
191
+ (DELETED, _("Deleted")),
178
192
  )
179
193
  slug = models.SlugField(db_index=True)
180
194
  center = models.PointField(geography=True, verbose_name=_("center"))
@@ -250,13 +264,17 @@ class Map(NamedModel):
250
264
  "hash": False,
251
265
  "scrollWheelZoom": False,
252
266
  "noControl": True,
253
- "umap_id": self.pk,
267
+ "id": self.pk,
254
268
  "schema": self.extra_schema,
255
269
  "slideshow": {},
256
270
  }
257
271
  )
258
272
  return map_settings
259
273
 
274
+ def move_to_trash(self):
275
+ self.share_status = Map.DELETED
276
+ self.save()
277
+
260
278
  def delete(self, **kwargs):
261
279
  # Explicitely call datalayers.delete, so we can deal with removing files
262
280
  # (the cascade delete would not call the model delete method)
@@ -270,7 +288,7 @@ class Map(NamedModel):
270
288
  umapjson["uri"] = request.build_absolute_uri(self.get_absolute_url())
271
289
  datalayers = []
272
290
  for datalayer in self.datalayer_set.all():
273
- with open(datalayer.geojson.path, "rb") as f:
291
+ with datalayer.geojson.open("rb") as f:
274
292
  layer = json.loads(f.read())
275
293
  if datalayer.settings:
276
294
  layer["_umap_options"] = datalayer.settings
@@ -352,19 +370,20 @@ class Map(NamedModel):
352
370
  return can
353
371
 
354
372
  def can_view(self, request):
355
- if self.share_status == self.BLOCKED:
373
+ if self.share_status in [Map.BLOCKED, Map.DELETED]:
356
374
  can = False
357
- elif self.owner is None:
358
- can = True
359
- elif self.share_status in [self.PUBLIC, self.OPEN]:
375
+ elif self.share_status in [Map.PUBLIC, Map.OPEN]:
360
376
  can = True
377
+ elif self.owner is None:
378
+ can = settings.UMAP_ALLOW_ANONYMOUS and self.is_anonymous_owner(request)
361
379
  elif not request.user.is_authenticated:
362
380
  can = False
363
381
  elif request.user == self.owner:
364
382
  can = True
365
383
  else:
384
+ restricted = self.share_status in [Map.PRIVATE, Map.DRAFT]
366
385
  can = not (
367
- self.share_status == self.PRIVATE
386
+ restricted
368
387
  and request.user not in self.editors.all()
369
388
  and self.team not in request.user.teams.all()
370
389
  )
@@ -423,10 +442,11 @@ class Pictogram(NamedModel):
423
442
  # Must be out of Datalayer for Django migration to run, because of python 2
424
443
  # serialize limitations.
425
444
  def upload_to(instance, filename):
426
- if instance.pk:
427
- return instance.upload_to()
428
- name = "%s.geojson" % slugify(instance.name)[:50] or "untitled"
429
- return os.path.join(instance.storage_root(), name)
445
+ return instance.geojson.storage.make_filename(instance)
446
+
447
+
448
+ def set_storage():
449
+ return storages["data"]
430
450
 
431
451
 
432
452
  class DataLayer(NamedModel):
@@ -444,13 +464,18 @@ class DataLayer(NamedModel):
444
464
  (COLLABORATORS, _("Editors and team only")),
445
465
  (OWNER, _("Owner only")),
446
466
  )
447
- uuid = models.UUIDField(
448
- unique=True, primary_key=True, default=uuid.uuid4, editable=False
467
+ ANONYMOUS_EDIT_STATUS = (
468
+ (INHERIT, _("Inherit")),
469
+ (OWNER, _("Only editable with secret edit link")),
470
+ (ANONYMOUS, _("Everyone can edit")),
449
471
  )
472
+ uuid = models.UUIDField(unique=True, primary_key=True, editable=False)
450
473
  old_id = models.IntegerField(null=True, blank=True)
451
474
  map = models.ForeignKey(Map, on_delete=models.CASCADE)
452
475
  description = models.TextField(blank=True, null=True, verbose_name=_("description"))
453
- geojson = models.FileField(upload_to=upload_to, blank=True, null=True)
476
+ geojson = models.FileField(
477
+ upload_to=upload_to, blank=True, null=True, storage=set_storage
478
+ )
454
479
  display_on_load = models.BooleanField(
455
480
  default=False,
456
481
  verbose_name=_("display on load"),
@@ -469,50 +494,16 @@ class DataLayer(NamedModel):
469
494
  class Meta:
470
495
  ordering = ("rank",)
471
496
 
472
- def save(self, force_insert=False, force_update=False, **kwargs):
473
- is_new = not bool(self.pk)
474
- super(DataLayer, self).save(
475
- force_insert=force_insert, force_update=force_update, **kwargs
476
- )
477
-
478
- if is_new:
479
- force_insert, force_update = False, True
480
- filename = self.upload_to()
481
- old_name = self.geojson.name
482
- new_name = self.geojson.storage.save(filename, self.geojson)
483
- self.geojson.storage.delete(old_name)
484
- self.geojson.name = new_name
485
- super(DataLayer, self).save(
486
- force_insert=force_insert, force_update=force_update, **kwargs
487
- )
488
- self.purge_gzip()
489
- self.purge_old_versions()
497
+ def save(self, **kwargs):
498
+ super(DataLayer, self).save(**kwargs)
499
+ self.geojson.storage.onDatalayerSave(self)
500
+ if hasattr(self, "_reference_version"):
501
+ del self._reference_version
490
502
 
491
503
  def delete(self, **kwargs):
492
- self.purge_gzip()
493
- self.to_purgatory()
504
+ self.geojson.storage.onDatalayerDelete(self)
494
505
  return super().delete(**kwargs)
495
506
 
496
- def to_purgatory(self):
497
- dest = Path(settings.UMAP_PURGATORY_ROOT)
498
- dest.mkdir(parents=True, exist_ok=True)
499
- src = Path(self.geojson.storage.location) / self.storage_root()
500
- for version in self.versions:
501
- name = version["name"]
502
- shutil.move(src / name, dest / f"{self.map.pk}_{name}")
503
-
504
- def upload_to(self):
505
- root = self.storage_root()
506
- name = "%s_%s.geojson" % (self.pk, int(time.time() * 1000))
507
- return os.path.join(root, name)
508
-
509
- def storage_root(self):
510
- path = ["datalayer", str(self.map.pk)[-1]]
511
- if len(str(self.map.pk)) > 1:
512
- path.append(str(self.map.pk)[-2])
513
- path.append(str(self.map.pk))
514
- return os.path.join(*path)
515
-
516
507
  def metadata(self, request=None):
517
508
  # Retrocompat: minimal settings for maps not saved after settings property
518
509
  # has been introduced
@@ -525,73 +516,34 @@ class DataLayer(NamedModel):
525
516
  obj["id"] = self.pk
526
517
  obj["permissions"] = {"edit_status": self.edit_status}
527
518
  obj["editMode"] = "advanced" if self.can_edit(request) else "disabled"
519
+ obj["_referenceVersion"] = self.reference_version
528
520
  return obj
529
521
 
530
522
  def clone(self, map_inst=None):
531
523
  new = self.__class__.objects.get(pk=self.pk)
532
524
  new._state.adding = True
533
- new.pk = None
525
+ new.pk = uuid.uuid4()
534
526
  if map_inst:
535
527
  new.map = map_inst
536
- new.geojson = File(new.geojson.file.file)
528
+ new.geojson = File(new.geojson.file.file, name="tmpname")
537
529
  new.save()
538
530
  return new
539
531
 
540
- def is_valid_version(self, name):
541
- valid_prefixes = [name.startswith("%s_" % self.pk)]
542
- if self.old_id:
543
- valid_prefixes.append(name.startswith("%s_" % self.old_id))
544
- return any(valid_prefixes) and name.endswith(".geojson")
545
-
546
- def version_metadata(self, name):
547
- els = name.split(".")[0].split("_")
548
- return {
549
- "name": name,
550
- "at": els[1],
551
- "size": self.geojson.storage.size(self.get_version_path(name)),
552
- }
532
+ @property
533
+ def reference_version(self):
534
+ if not hasattr(self, "_reference_version"):
535
+ self._reference_version = self.geojson.storage.get_reference_version(self)
536
+ return self._reference_version
553
537
 
554
538
  @property
555
539
  def versions(self):
556
- root = self.storage_root()
557
- names = self.geojson.storage.listdir(root)[1]
558
- names = [name for name in names if self.is_valid_version(name)]
559
- versions = [self.version_metadata(name) for name in names]
560
- versions.sort(reverse=True, key=operator.itemgetter("at"))
561
- return versions
562
-
563
- def get_version(self, name):
564
- path = self.get_version_path(name)
565
- with self.geojson.storage.open(path, "r") as f:
566
- return f.read()
567
-
568
- def get_version_path(self, name):
569
- return "{root}/{name}".format(root=self.storage_root(), name=name)
570
-
571
- def purge_old_versions(self):
572
- root = self.storage_root()
573
- versions = self.versions[settings.UMAP_KEEP_VERSIONS :]
574
- for version in versions:
575
- name = version["name"]
576
- # Should not be in the list, but ensure to not delete the file
577
- # currently used in database
578
- if self.geojson.name.endswith(name):
579
- continue
580
- try:
581
- self.geojson.storage.delete(os.path.join(root, name))
582
- except FileNotFoundError:
583
- pass
584
-
585
- def purge_gzip(self):
586
- root = self.storage_root()
587
- names = self.geojson.storage.listdir(root)[1]
588
- prefixes = [f"{self.pk}_"]
589
- if self.old_id:
590
- prefixes.append(f"{self.old_id}_")
591
- prefixes = tuple(prefixes)
592
- for name in names:
593
- if name.startswith(prefixes) and name.endswith(".gz"):
594
- self.geojson.storage.delete(os.path.join(root, name))
540
+ return self.geojson.storage.list_versions(self)
541
+
542
+ def get_version(self, ref):
543
+ return self.geojson.storage.get_version(ref, self)
544
+
545
+ def get_version_path(self, ref):
546
+ return self.geojson.storage.get_version_path(ref, self)
595
547
 
596
548
  def can_edit(self, request=None):
597
549
  """
umap/settings/base.py CHANGED
@@ -175,10 +175,25 @@ STORAGES = {
175
175
  "default": {
176
176
  "BACKEND": "django.core.files.storage.FileSystemStorage",
177
177
  },
178
+ "data": {
179
+ "BACKEND": "umap.storage.UmapFileSystem",
180
+ },
178
181
  "staticfiles": {
179
182
  "BACKEND": "umap.storage.UmapManifestStaticFilesStorage",
180
183
  },
181
184
  }
185
+ # Add application/json and application/geo+json to default django-storages setting
186
+ # in order to gzip our datalayers geojson files.
187
+ GZIP_CONTENT_TYPES = [
188
+ "text/css",
189
+ "text/javascript",
190
+ "application/javascript",
191
+ "application/x-javascript",
192
+ "image/svg+xml",
193
+ "application/json",
194
+ "application/geo+json",
195
+ ]
196
+
182
197
 
183
198
  # =============================================================================
184
199
  # Templates
@@ -260,14 +275,19 @@ UMAP_MAPS_PER_PAGE_OWNER = 10
260
275
  UMAP_SEARCH_CONFIGURATION = "simple"
261
276
  UMAP_HELP_URL = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help"
262
277
  USER_MAPS_URL = "user_maps"
263
- DATABASES = {"default": env.db(default="postgis://localhost:5432/umap")}
278
+ DATABASES = {
279
+ "default": env.db(
280
+ default="postgis://localhost:5432/umap",
281
+ engine="django.contrib.gis.db.backends.postgis",
282
+ )
283
+ }
264
284
  UMAP_DEFAULT_SHARE_STATUS = None
265
285
  UMAP_DEFAULT_EDIT_STATUS = None
266
286
  UMAP_DEFAULT_FEATURES_HAVE_OWNERS = False
267
287
  UMAP_HOME_FEED = "latest"
268
288
  UMAP_IMPORTERS = {}
269
289
  UMAP_HOST_INFOS = {}
270
- UMAP_PURGATORY_ROOT = "/tmp/umappurgatory"
290
+ UMAP_LABEL_KEYS = ["name", "title"]
271
291
 
272
292
  UMAP_READONLY = env("UMAP_READONLY", default=False)
273
293
  UMAP_GZIP = True