umap-project 2.8.2__py3-none-any.whl → 2.9.0b0__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 (157) hide show
  1. umap/__init__.py +1 -1
  2. umap/asgi.py +12 -7
  3. umap/context_processors.py +1 -0
  4. umap/locale/en/LC_MESSAGES/django.po +102 -59
  5. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/fr/LC_MESSAGES/django.po +105 -61
  7. umap/management/commands/empty_trash.py +12 -1
  8. umap/migrations/0026_datalayer_modified_at_datalayer_share_status.py +26 -0
  9. umap/models.py +23 -3
  10. umap/settings/base.py +4 -1
  11. umap/static/umap/base.css +1 -1
  12. umap/static/umap/content.css +2 -22
  13. umap/static/umap/css/bar.css +7 -10
  14. umap/static/umap/css/form.css +28 -29
  15. umap/static/umap/css/icon.css +8 -2
  16. umap/static/umap/css/panel.css +2 -1
  17. umap/static/umap/css/tooltip.css +33 -31
  18. umap/static/umap/img/16-white.svg +2 -0
  19. umap/static/umap/img/16.svg +1 -1
  20. umap/static/umap/img/providers/bitbucket.png +0 -0
  21. umap/static/umap/img/providers/github.png +0 -0
  22. umap/static/umap/img/providers/keycloak.png +0 -0
  23. umap/static/umap/img/providers/openstreetmap-oauth2.png +0 -0
  24. umap/static/umap/img/providers/twitter-oauth2.png +0 -0
  25. umap/static/umap/img/source/16-white.svg +3 -1
  26. umap/static/umap/img/source/16.svg +1 -1
  27. umap/static/umap/js/components/alerts/alert.js +4 -1
  28. umap/static/umap/js/modules/browser.js +6 -6
  29. umap/static/umap/js/modules/caption.js +30 -7
  30. umap/static/umap/js/modules/data/features.js +21 -24
  31. umap/static/umap/js/modules/data/layer.js +71 -33
  32. umap/static/umap/js/modules/form/builder.js +241 -0
  33. umap/static/umap/js/modules/form/fields.js +1338 -0
  34. umap/static/umap/js/modules/formatter.js +5 -8
  35. umap/static/umap/js/modules/help.js +3 -1
  36. umap/static/umap/js/modules/importer.js +1 -1
  37. umap/static/umap/js/modules/permissions.js +5 -4
  38. umap/static/umap/js/modules/rendering/icon.js +5 -1
  39. umap/static/umap/js/modules/rendering/layers/classified.js +11 -7
  40. umap/static/umap/js/modules/rendering/layers/cluster.js +11 -1
  41. umap/static/umap/js/modules/rendering/map.js +0 -2
  42. umap/static/umap/js/modules/rules.js +2 -1
  43. umap/static/umap/js/modules/schema.js +5 -6
  44. umap/static/umap/js/modules/share.js +3 -3
  45. umap/static/umap/js/modules/sync/engine.js +18 -13
  46. umap/static/umap/js/modules/sync/updaters.js +8 -0
  47. umap/static/umap/js/modules/sync/websocket.js +10 -5
  48. umap/static/umap/js/modules/tableeditor.js +3 -2
  49. umap/static/umap/js/modules/ui/bar.js +17 -9
  50. umap/static/umap/js/modules/ui/base.js +7 -24
  51. umap/static/umap/js/modules/ui/tooltip.js +19 -11
  52. umap/static/umap/js/modules/umap.js +36 -24
  53. umap/static/umap/js/modules/utils.js +196 -12
  54. umap/static/umap/js/umap.controls.js +0 -12
  55. umap/static/umap/locale/br.js +21 -13
  56. umap/static/umap/locale/br.json +21 -13
  57. umap/static/umap/locale/ca.js +12 -4
  58. umap/static/umap/locale/ca.json +12 -4
  59. umap/static/umap/locale/cs_CZ.js +10 -4
  60. umap/static/umap/locale/cs_CZ.json +10 -4
  61. umap/static/umap/locale/de.js +12 -4
  62. umap/static/umap/locale/de.json +12 -4
  63. umap/static/umap/locale/el.js +12 -4
  64. umap/static/umap/locale/el.json +12 -4
  65. umap/static/umap/locale/en.js +9 -4
  66. umap/static/umap/locale/en.json +9 -4
  67. umap/static/umap/locale/es.js +20 -12
  68. umap/static/umap/locale/es.json +20 -12
  69. umap/static/umap/locale/eu.js +12 -4
  70. umap/static/umap/locale/eu.json +12 -4
  71. umap/static/umap/locale/fa_IR.js +12 -4
  72. umap/static/umap/locale/fa_IR.json +12 -4
  73. umap/static/umap/locale/fr.js +10 -5
  74. umap/static/umap/locale/fr.json +10 -5
  75. umap/static/umap/locale/gl.js +353 -345
  76. umap/static/umap/locale/gl.json +353 -345
  77. umap/static/umap/locale/hu.js +9 -4
  78. umap/static/umap/locale/hu.json +9 -4
  79. umap/static/umap/locale/it.js +100 -92
  80. umap/static/umap/locale/it.json +100 -92
  81. umap/static/umap/locale/ms.js +12 -4
  82. umap/static/umap/locale/ms.json +12 -4
  83. umap/static/umap/locale/nl.js +12 -4
  84. umap/static/umap/locale/nl.json +12 -4
  85. umap/static/umap/locale/pl.js +12 -4
  86. umap/static/umap/locale/pl.json +12 -4
  87. umap/static/umap/locale/pt.js +12 -4
  88. umap/static/umap/locale/pt.json +12 -4
  89. umap/static/umap/locale/pt_PT.js +12 -4
  90. umap/static/umap/locale/pt_PT.json +12 -4
  91. umap/static/umap/locale/th_TH.js +12 -4
  92. umap/static/umap/locale/th_TH.json +12 -4
  93. umap/static/umap/locale/zh_TW.js +10 -4
  94. umap/static/umap/locale/zh_TW.json +10 -4
  95. umap/static/umap/map.css +12 -8
  96. umap/static/umap/nav.css +2 -3
  97. umap/static/umap/unittests/utils.js +14 -0
  98. umap/static/umap/vars.css +2 -0
  99. umap/sync/__init__.py +0 -0
  100. umap/sync/app.py +181 -0
  101. umap/sync/payloads.py +49 -0
  102. umap/templates/auth/user_detail.html +4 -0
  103. umap/templates/auth/user_form.html +9 -6
  104. umap/templates/auth/user_stars.html +4 -0
  105. umap/templates/base.html +1 -1
  106. umap/templates/registration/login.html +2 -5
  107. umap/templates/umap/about.html +5 -0
  108. umap/templates/umap/about_summary.html +2 -2
  109. umap/templates/umap/components/provider.html +8 -0
  110. umap/templates/umap/js.html +0 -3
  111. umap/templates/umap/map_detail.html +1 -1
  112. umap/templates/umap/password_change.html +4 -0
  113. umap/templates/umap/password_change_done.html +4 -0
  114. umap/templates/umap/search.html +4 -0
  115. umap/templates/umap/team_confirm_delete.html +4 -0
  116. umap/templates/umap/team_detail.html +4 -0
  117. umap/templates/umap/team_form.html +4 -0
  118. umap/templates/umap/user_dashboard.html +1 -1
  119. umap/templates/umap/user_teams.html +4 -0
  120. umap/tests/base.py +3 -1
  121. umap/tests/integration/conftest.py +16 -23
  122. umap/tests/integration/test_basics.py +2 -2
  123. umap/tests/integration/test_caption.py +1 -0
  124. umap/tests/integration/test_draw_polygon.py +3 -3
  125. umap/tests/integration/test_edit_datalayer.py +1 -1
  126. umap/tests/integration/test_edit_map.py +3 -3
  127. umap/tests/integration/test_edit_polygon.py +1 -1
  128. umap/tests/integration/test_import.py +23 -1
  129. umap/tests/integration/test_optimistic_merge.py +1 -0
  130. umap/tests/integration/test_picto.py +8 -8
  131. umap/tests/integration/test_save.py +1 -0
  132. umap/tests/integration/test_star.py +13 -9
  133. umap/tests/integration/test_tableeditor.py +1 -0
  134. umap/tests/integration/test_websocket_sync.py +112 -33
  135. umap/tests/settings.py +2 -0
  136. umap/tests/test_datalayer.py +2 -3
  137. umap/tests/test_datalayer_views.py +20 -1
  138. umap/tests/test_empty_trash.py +10 -3
  139. umap/tests/test_map_views.py +11 -0
  140. umap/utils.py +24 -11
  141. umap/views.py +37 -6
  142. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/METADATA +15 -15
  143. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/RECORD +146 -145
  144. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/WHEEL +1 -1
  145. umap/management/commands/run_websocket_server.py +0 -23
  146. umap/settings/local_s3.py +0 -45
  147. umap/static/umap/bitbucket.png +0 -0
  148. umap/static/umap/github.png +0 -0
  149. umap/static/umap/js/umap.forms.js +0 -1242
  150. umap/static/umap/keycloak.png +0 -0
  151. umap/static/umap/openstreetmap.png +0 -0
  152. umap/static/umap/twitter.png +0 -0
  153. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +0 -468
  154. umap/tests/test_websocket_server.py +0 -22
  155. umap/websocket_server.py +0 -202
  156. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/entry_points.txt +0 -0
  157. {umap_project-2.8.2.dist-info → umap_project-2.9.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -19,7 +19,6 @@ const locale = {
19
19
  "Add a new property": "Adicionar uma nova propriedade",
20
20
  "Add a polygon to the current multi": "Adicionar um polígono para o multi atual",
21
21
  "Add image URL": "Adicionar URL da imagem",
22
- "Add": "Adicionar",
23
22
  "Advanced actions": "Ações avançadas",
24
23
  "Advanced properties": "Propriedades avançadas",
25
24
  "All data and settings of the map": "Todos os dados e definições do mapa",
@@ -120,7 +119,6 @@ const locale = {
120
119
  "Display the locate control": "Mostrar o controlo de localizar",
121
120
  "Display the measure control": "Mostrar o controlo de medição",
122
121
  "Display the search control": "Mostrar o controlo de pesquisa",
123
- "Display the star map button": "Mostrar o botão de marcar como favorito",
124
122
  "Display the tile layers control": "Mostrar o controlo de camadas de telas",
125
123
  "Display the zoom control": "Mostrar o controlo de aproximar e afastar (zoom)",
126
124
  "Do you want to display a caption bar?": "Mostrar uma barra de cabeçalho?",
@@ -516,11 +514,21 @@ const locale = {
516
514
  "show/hide all layers": "show/hide all layers",
517
515
  "zoom to data extent": "zoom to data extent",
518
516
  "download visible data": "download visible data",
519
- "{connectedPeers} peer(s) currently connected to this map": "{connectedPeers} peer(s) currently connected to this map",
520
517
  "Import helpers": "Import helpers",
521
518
  "Import helpers will fill the URL field for you.": "Import helpers will fill the URL field for you.",
522
519
  "Wikipedia": "Wikipedia",
523
- "Save draft": "Save draft"
520
+ "Save draft": "Save draft",
521
+ "No data has been found for import": "No data has been found for import",
522
+ "Successfully imported {count} feature(s)": "Successfully imported {count} feature(s)",
523
+ "Disconnected": "Disconnected",
524
+ "You must be logged in": "You must be logged in",
525
+ "Created at {date}": "Created at {date}",
526
+ "Modified at {date}": "Modified at {date}",
527
+ "on hover": "on hover",
528
+ "Cannot load remote data for layer \"{layer}\" with url \"{url}\"": "Cannot load remote data for layer \"{layer}\" with url \"{url}\"",
529
+ "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"": "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"",
530
+ "Import failed: invalid data": "Import failed: invalid data",
531
+ "Anonymous": "Anonymous"
524
532
  }
525
533
  L.registerLocale("pt_PT", locale)
526
534
  L.setLocale("pt_PT")
@@ -19,7 +19,6 @@
19
19
  "Add a new property": "Adicionar uma nova propriedade",
20
20
  "Add a polygon to the current multi": "Adicionar um polígono para o multi atual",
21
21
  "Add image URL": "Adicionar URL da imagem",
22
- "Add": "Adicionar",
23
22
  "Advanced actions": "Ações avançadas",
24
23
  "Advanced properties": "Propriedades avançadas",
25
24
  "All data and settings of the map": "Todos os dados e definições do mapa",
@@ -120,7 +119,6 @@
120
119
  "Display the locate control": "Mostrar o controlo de localizar",
121
120
  "Display the measure control": "Mostrar o controlo de medição",
122
121
  "Display the search control": "Mostrar o controlo de pesquisa",
123
- "Display the star map button": "Mostrar o botão de marcar como favorito",
124
122
  "Display the tile layers control": "Mostrar o controlo de camadas de telas",
125
123
  "Display the zoom control": "Mostrar o controlo de aproximar e afastar (zoom)",
126
124
  "Do you want to display a caption bar?": "Mostrar uma barra de cabeçalho?",
@@ -516,9 +514,19 @@
516
514
  "show/hide all layers": "show/hide all layers",
517
515
  "zoom to data extent": "zoom to data extent",
518
516
  "download visible data": "download visible data",
519
- "{connectedPeers} peer(s) currently connected to this map": "{connectedPeers} peer(s) currently connected to this map",
520
517
  "Import helpers": "Import helpers",
521
518
  "Import helpers will fill the URL field for you.": "Import helpers will fill the URL field for you.",
522
519
  "Wikipedia": "Wikipedia",
523
- "Save draft": "Save draft"
520
+ "Save draft": "Save draft",
521
+ "No data has been found for import": "No data has been found for import",
522
+ "Successfully imported {count} feature(s)": "Successfully imported {count} feature(s)",
523
+ "Disconnected": "Disconnected",
524
+ "You must be logged in": "You must be logged in",
525
+ "Created at {date}": "Created at {date}",
526
+ "Modified at {date}": "Modified at {date}",
527
+ "on hover": "on hover",
528
+ "Cannot load remote data for layer \"{layer}\" with url \"{url}\"": "Cannot load remote data for layer \"{layer}\" with url \"{url}\"",
529
+ "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"": "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"",
530
+ "Import failed: invalid data": "Import failed: invalid data",
531
+ "Anonymous": "Anonymous"
524
532
  }
@@ -19,7 +19,6 @@ const locale = {
19
19
  "Add a new property": "เพิ่มคุณสมบัติใหม่",
20
20
  "Add a polygon to the current multi": "เพิ่มรูปร่างให้กับปัจจุบันหลายปัจจุบัน",
21
21
  "Add image URL": "เพิ่ม URL รูปภาพ",
22
- "Add": "เพิ่ม",
23
22
  "Advanced actions": "Advanced actions",
24
23
  "Advanced properties": "Advanced properties",
25
24
  "All data and settings of the map": "ข้อมูลและการตั้งค่าทั้งหมดของแผนที่",
@@ -120,7 +119,6 @@ const locale = {
120
119
  "Display the locate control": "Display the locate control",
121
120
  "Display the measure control": "Display the measure control",
122
121
  "Display the search control": "Display the search control",
123
- "Display the star map button": "Display the star map button",
124
122
  "Display the tile layers control": "Display the tile layers control",
125
123
  "Display the zoom control": "Display the zoom control",
126
124
  "Do you want to display a caption bar?": "Do you want to display a caption bar?",
@@ -516,11 +514,21 @@ const locale = {
516
514
  "show/hide all layers": "show/hide all layers",
517
515
  "zoom to data extent": "zoom to data extent",
518
516
  "download visible data": "download visible data",
519
- "{connectedPeers} peer(s) currently connected to this map": "{connectedPeers} peer(s) currently connected to this map",
520
517
  "Import helpers": "Import helpers",
521
518
  "Import helpers will fill the URL field for you.": "Import helpers will fill the URL field for you.",
522
519
  "Wikipedia": "Wikipedia",
523
- "Save draft": "Save draft"
520
+ "Save draft": "Save draft",
521
+ "No data has been found for import": "No data has been found for import",
522
+ "Successfully imported {count} feature(s)": "Successfully imported {count} feature(s)",
523
+ "Disconnected": "Disconnected",
524
+ "You must be logged in": "You must be logged in",
525
+ "Created at {date}": "Created at {date}",
526
+ "Modified at {date}": "Modified at {date}",
527
+ "on hover": "on hover",
528
+ "Cannot load remote data for layer \"{layer}\" with url \"{url}\"": "Cannot load remote data for layer \"{layer}\" with url \"{url}\"",
529
+ "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"": "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"",
530
+ "Import failed: invalid data": "Import failed: invalid data",
531
+ "Anonymous": "Anonymous"
524
532
  }
525
533
  L.registerLocale("th_TH", locale)
526
534
  L.setLocale("th_TH")
@@ -19,7 +19,6 @@
19
19
  "Add a new property": "เพิ่มคุณสมบัติใหม่",
20
20
  "Add a polygon to the current multi": "เพิ่มรูปร่างให้กับปัจจุบันหลายปัจจุบัน",
21
21
  "Add image URL": "เพิ่ม URL รูปภาพ",
22
- "Add": "เพิ่ม",
23
22
  "Advanced actions": "Advanced actions",
24
23
  "Advanced properties": "Advanced properties",
25
24
  "All data and settings of the map": "ข้อมูลและการตั้งค่าทั้งหมดของแผนที่",
@@ -120,7 +119,6 @@
120
119
  "Display the locate control": "Display the locate control",
121
120
  "Display the measure control": "Display the measure control",
122
121
  "Display the search control": "Display the search control",
123
- "Display the star map button": "Display the star map button",
124
122
  "Display the tile layers control": "Display the tile layers control",
125
123
  "Display the zoom control": "Display the zoom control",
126
124
  "Do you want to display a caption bar?": "Do you want to display a caption bar?",
@@ -516,9 +514,19 @@
516
514
  "show/hide all layers": "show/hide all layers",
517
515
  "zoom to data extent": "zoom to data extent",
518
516
  "download visible data": "download visible data",
519
- "{connectedPeers} peer(s) currently connected to this map": "{connectedPeers} peer(s) currently connected to this map",
520
517
  "Import helpers": "Import helpers",
521
518
  "Import helpers will fill the URL field for you.": "Import helpers will fill the URL field for you.",
522
519
  "Wikipedia": "Wikipedia",
523
- "Save draft": "Save draft"
520
+ "Save draft": "Save draft",
521
+ "No data has been found for import": "No data has been found for import",
522
+ "Successfully imported {count} feature(s)": "Successfully imported {count} feature(s)",
523
+ "Disconnected": "Disconnected",
524
+ "You must be logged in": "You must be logged in",
525
+ "Created at {date}": "Created at {date}",
526
+ "Modified at {date}": "Modified at {date}",
527
+ "on hover": "on hover",
528
+ "Cannot load remote data for layer \"{layer}\" with url \"{url}\"": "Cannot load remote data for layer \"{layer}\" with url \"{url}\"",
529
+ "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"": "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"",
530
+ "Import failed: invalid data": "Import failed: invalid data",
531
+ "Anonymous": "Anonymous"
524
532
  }
@@ -19,7 +19,6 @@ const locale = {
19
19
  "Add a new property": "新增屬性",
20
20
  "Add a polygon to the current multi": "新增多邊形",
21
21
  "Add image URL": "新增圖片URL",
22
- "Add": "新增",
23
22
  "Advanced actions": "進階動作",
24
23
  "Advanced properties": "進階屬性",
25
24
  "All data and settings of the map": "地圖的所有資料與設定",
@@ -120,7 +119,6 @@ const locale = {
120
119
  "Display the locate control": "顯示定位鍵",
121
120
  "Display the measure control": "顯示比例尺鍵",
122
121
  "Display the search control": "顯示搜尋鍵",
123
- "Display the star map button": "顯示星號地圖按鈕",
124
122
  "Display the tile layers control": "顯示圖層鍵",
125
123
  "Display the zoom control": "顯示縮放鍵",
126
124
  "Do you want to display a caption bar?": "您是否要顯示標題列?",
@@ -516,13 +514,21 @@ const locale = {
516
514
  "show/hide all layers": "顯示/隱藏所有圖層",
517
515
  "zoom to data extent": "切換至資料範圍",
518
516
  "download visible data": "下載可視資料",
519
- "{connectedPeers} peer(s) currently connected to this map": "這份地圖已經有 {connectedPeers} 伙伴連線",
520
517
  "Import helpers": "匯入幫手",
521
518
  "Import helpers will fill the URL field for you.": "匯入幫會為你填入網址",
522
519
  "Wikipedia": "維基百科",
523
520
  "Save draft": "儲存草稿",
524
521
  "No data has been found for import": "匯入時沒有找到資料",
525
- "Successfully imported {count} feature(s)": "成功匯入 {count} 圖徵"
522
+ "Successfully imported {count} feature(s)": "成功匯入 {count} 圖徵",
523
+ "Disconnected": "Disconnected",
524
+ "You must be logged in": "You must be logged in",
525
+ "Created at {date}": "Created at {date}",
526
+ "Modified at {date}": "Modified at {date}",
527
+ "on hover": "on hover",
528
+ "Cannot load remote data for layer \"{layer}\" with url \"{url}\"": "Cannot load remote data for layer \"{layer}\" with url \"{url}\"",
529
+ "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"": "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"",
530
+ "Import failed: invalid data": "Import failed: invalid data",
531
+ "Anonymous": "Anonymous"
526
532
  }
527
533
  L.registerLocale("zh_TW", locale)
528
534
  L.setLocale("zh_TW")
@@ -19,7 +19,6 @@
19
19
  "Add a new property": "新增屬性",
20
20
  "Add a polygon to the current multi": "新增多邊形",
21
21
  "Add image URL": "新增圖片URL",
22
- "Add": "新增",
23
22
  "Advanced actions": "進階動作",
24
23
  "Advanced properties": "進階屬性",
25
24
  "All data and settings of the map": "地圖的所有資料與設定",
@@ -120,7 +119,6 @@
120
119
  "Display the locate control": "顯示定位鍵",
121
120
  "Display the measure control": "顯示比例尺鍵",
122
121
  "Display the search control": "顯示搜尋鍵",
123
- "Display the star map button": "顯示星號地圖按鈕",
124
122
  "Display the tile layers control": "顯示圖層鍵",
125
123
  "Display the zoom control": "顯示縮放鍵",
126
124
  "Do you want to display a caption bar?": "您是否要顯示標題列?",
@@ -516,11 +514,19 @@
516
514
  "show/hide all layers": "顯示/隱藏所有圖層",
517
515
  "zoom to data extent": "切換至資料範圍",
518
516
  "download visible data": "下載可視資料",
519
- "{connectedPeers} peer(s) currently connected to this map": "這份地圖已經有 {connectedPeers} 伙伴連線",
520
517
  "Import helpers": "匯入幫手",
521
518
  "Import helpers will fill the URL field for you.": "匯入幫會為你填入網址",
522
519
  "Wikipedia": "維基百科",
523
520
  "Save draft": "儲存草稿",
524
521
  "No data has been found for import": "匯入時沒有找到資料",
525
- "Successfully imported {count} feature(s)": "成功匯入 {count} 圖徵"
522
+ "Successfully imported {count} feature(s)": "成功匯入 {count} 圖徵",
523
+ "Disconnected": "Disconnected",
524
+ "You must be logged in": "You must be logged in",
525
+ "Created at {date}": "Created at {date}",
526
+ "Modified at {date}": "Modified at {date}",
527
+ "on hover": "on hover",
528
+ "Cannot load remote data for layer \"{layer}\" with url \"{url}\"": "Cannot load remote data for layer \"{layer}\" with url \"{url}\"",
529
+ "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"": "Cannot parse remote data for layer \"{layer}\" with url \"{url}\"",
530
+ "Import failed: invalid data": "Import failed: invalid data",
531
+ "Anonymous": "Anonymous"
526
532
  }
umap/static/umap/map.css CHANGED
@@ -134,12 +134,6 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
134
134
  background-position: -72px -144px;
135
135
  box-shadow: 0 0 4px 0 black inset;
136
136
  }
137
- .leaflet-control-star [type="button"] {
138
- background-position: -144px -144px;
139
- }
140
- .leaflet-control-star.starred [type="button"] {
141
- background-position: -108px -144px;
142
- }
143
137
  .leaflet-control-search [type="button"] {
144
138
  background-position: -36px -108px;
145
139
  display: block;
@@ -694,8 +688,18 @@ a.umap-control-caption,
694
688
  .datalayer-name {
695
689
  cursor: pointer;
696
690
  }
697
- .umap-caption .umap-map-author {
698
- padding-inline-start: 31px;
691
+ .umap-caption .dates {
692
+ color: var(--color-mediumGray);
693
+ }
694
+ .umap-caption .header {
695
+ display: flex;
696
+ }
697
+ .umap-caption .header i.icon {
698
+ flex-shrink: 0;
699
+ }
700
+ .umap-caption hgroup p,
701
+ .umap-caption hgroup button {
702
+ margin: 0;
699
703
  }
700
704
  .umap-browser .main-toolbox {
701
705
  padding-left: 4px; /* Align with toolbox below */
umap/static/umap/nav.css CHANGED
@@ -41,7 +41,7 @@ footer .i18n_switch {
41
41
  display: flex;
42
42
  flex-direction: column;
43
43
  }
44
- .umap-nav a {
44
+ .umap-nav a:not(.button) {
45
45
  color: var(--color-darkBlue);
46
46
  padding: .4rem;
47
47
  }
@@ -51,7 +51,7 @@ footer .i18n_switch {
51
51
  .umap-nav h1 {
52
52
  margin-bottom: 0;
53
53
  }
54
- .umap-nav h1 a {
54
+ .umap-nav h1 a:not(.button) {
55
55
  background-image: url("./img/logo.svg");
56
56
  background-position: left center;
57
57
  background-repeat: no-repeat;
@@ -74,7 +74,6 @@ html[dir="rtl"] .umap-nav h1 a {
74
74
  .umap-nav .button,
75
75
  .umap-nav .button:hover {
76
76
  text-decoration: none;
77
- min-width: 150px;
78
77
  margin-inline-start: 1rem;
79
78
  }
80
79
 
@@ -54,6 +54,13 @@ describe('Utils', () => {
54
54
  )
55
55
  })
56
56
 
57
+ it('should handle links with another url in path', () => {
58
+ assert.equal(
59
+ Utils.toHTML('A simple https://osm.org/https://anotherurl.com link'),
60
+ 'A simple <a href="https://osm.org/https://anotherurl.com" target="_blank">https://osm.org/https://anotherurl.com</a> link'
61
+ )
62
+ })
63
+
57
64
  it('should handle simple link inside parenthesis', () => {
58
65
  assert.equal(
59
66
  Utils.toHTML('A simple link (http://osm.org)'),
@@ -172,6 +179,13 @@ describe('Utils', () => {
172
179
  )
173
180
  })
174
181
 
182
+ it('http link with http link as in path', () => {
183
+ assert.equal(
184
+ Utils.toHTML('A phrase with a [[https://iframeurl.com/https://another.com]].'),
185
+ 'A phrase with a <a href="https://iframeurl.com/https://another.com" target="_blank">https://iframeurl.com/https://another.com</a>.'
186
+ )
187
+ })
188
+
175
189
  it('simple bullet points', () => {
176
190
  assert.equal(
177
191
  Utils.toHTML('* First point\n* Second point\n* Last point'),
umap/static/umap/vars.css CHANGED
@@ -24,6 +24,7 @@
24
24
  --button-primary-color: var(--color-darkBlue);
25
25
  --button-neutral-background: var(--color-lightGray);
26
26
  --button-neutral-color: var(--color-darkGray);
27
+ --button-padding: 8px 16px;
27
28
 
28
29
  /* Sizes and spaces */
29
30
  --gutter: 8px;
@@ -38,6 +39,7 @@
38
39
  --control-size: 36px;
39
40
  --border-radius: 4px;
40
41
  --box-padding: 20px;
42
+ --small-box-padding: 4px;
41
43
  --box-margin: 14px;
42
44
  --text-margin: 7px;
43
45
 
umap/sync/__init__.py ADDED
File without changes
umap/sync/app.py ADDED
@@ -0,0 +1,181 @@
1
+ import asyncio
2
+ import logging
3
+
4
+ import redis.asyncio as redis
5
+ from django.conf import settings
6
+ from django.core.signing import TimestampSigner
7
+ from django.urls import path
8
+ from pydantic import ValidationError
9
+
10
+ from .payloads import (
11
+ JoinRequest,
12
+ JoinResponse,
13
+ ListPeersResponse,
14
+ OperationMessage,
15
+ PeerMessage,
16
+ Request,
17
+ )
18
+
19
+
20
+ async def application(scope, receive, send):
21
+ path = scope["path"].lstrip("/")
22
+ for pattern in urlpatterns:
23
+ if matched := pattern.resolve(path):
24
+ await matched.func(scope, receive, send, **matched.kwargs)
25
+ break
26
+ else:
27
+ await send({"type": "websocket.close"})
28
+
29
+
30
+ async def sync(scope, receive, send, **kwargs):
31
+ peer = Peer(kwargs["map_id"])
32
+ peer._send = send
33
+ while True:
34
+ event = await receive()
35
+
36
+ if event["type"] == "websocket.connect":
37
+ try:
38
+ await peer.connect()
39
+ await send({"type": "websocket.accept"})
40
+ except ValueError:
41
+ await send({"type": "websocket.close"})
42
+
43
+ if event["type"] == "websocket.disconnect":
44
+ await peer.disconnect()
45
+ break
46
+
47
+ if event["type"] == "websocket.receive":
48
+ if event["text"] == "ping":
49
+ await send({"type": "websocket.send", "text": "pong"})
50
+ else:
51
+ await peer.receive(event["text"])
52
+
53
+
54
+ class Peer:
55
+ def __init__(self, map_id, username=None):
56
+ self.username = username or ""
57
+ self.map_id = map_id
58
+ self.is_authenticated = False
59
+ self._subscriptions = []
60
+
61
+ @property
62
+ def room_key(self):
63
+ return f"umap:{self.map_id}"
64
+
65
+ @property
66
+ def peer_key(self):
67
+ return f"user:{self.map_id}:{self.peer_id}"
68
+
69
+ async def get_peers(self):
70
+ known = await self.client.hgetall(self.room_key)
71
+ active = await self.client.pubsub_channels(f"user:{self.map_id}:*")
72
+ if not active:
73
+ # Poor man way of deleting stale usernames from the store
74
+ # HEXPIRE command is not in the open source Redis version
75
+ await self.client.delete(self.room_key)
76
+ await self.store_username()
77
+ active = [name.split(b":")[-1] for name in active]
78
+ if self.peer_id.encode() not in active:
79
+ # Our connection may not yet be active
80
+ active.append(self.peer_id.encode())
81
+ return {k: v for k, v in known.items() if k in active}
82
+
83
+ async def store_username(self):
84
+ await self.client.hset(self.room_key, self.peer_id, self.username)
85
+
86
+ async def listen_to_channel(self, channel_name):
87
+ async def reader(pubsub):
88
+ await pubsub.subscribe(channel_name)
89
+ while True:
90
+ if pubsub.connection is None:
91
+ # It has been unsubscribed/closed.
92
+ break
93
+ try:
94
+ message = await pubsub.get_message(ignore_subscribe_messages=True)
95
+ except Exception as err:
96
+ print(err)
97
+ break
98
+ if message is not None:
99
+ await self.send(message["data"].decode())
100
+ await asyncio.sleep(0.001) # Be nice with the server
101
+
102
+ async with self.client.pubsub() as pubsub:
103
+ self._subscriptions.append(pubsub)
104
+ asyncio.create_task(reader(pubsub))
105
+
106
+ async def listen(self):
107
+ await self.listen_to_channel(self.room_key)
108
+ await self.listen_to_channel(self.peer_key)
109
+
110
+ async def connect(self):
111
+ self.client = redis.from_url(settings.REDIS_URL)
112
+
113
+ async def disconnect(self):
114
+ await self.client.hdel(self.room_key, self.peer_id)
115
+ for pubsub in self._subscriptions:
116
+ await pubsub.unsubscribe()
117
+ await pubsub.close()
118
+ await self.send_peers_list()
119
+ await self.client.aclose()
120
+
121
+ async def send_peers_list(self):
122
+ message = ListPeersResponse(peers=await self.get_peers())
123
+ await self.broadcast(message.model_dump_json())
124
+
125
+ async def broadcast(self, message):
126
+ print("BROADCASTING", message)
127
+ # Send to all channels (including sender!)
128
+ await self.client.publish(self.room_key, message)
129
+
130
+ async def send_to(self, peer_id, message):
131
+ print("SEND TO", peer_id, message)
132
+ # Send to one given channel
133
+ await self.client.publish(f"user:{self.map_id}:{peer_id}", message)
134
+
135
+ async def receive(self, text_data):
136
+ if not self.is_authenticated:
137
+ print("AUTHENTICATING", text_data)
138
+ message = JoinRequest.model_validate_json(text_data)
139
+ signed = TimestampSigner().unsign_object(message.token, max_age=30)
140
+ user, map_id, permissions = signed.values()
141
+ assert str(map_id) == self.map_id
142
+ if "edit" not in permissions:
143
+ return await self.disconnect()
144
+ self.peer_id = message.peer
145
+ self.username = message.username
146
+ print("AUTHENTICATED", self.peer_id)
147
+ await self.store_username()
148
+ await self.listen()
149
+ response = JoinResponse(peer=self.peer_id, peers=await self.get_peers())
150
+ await self.send(response.model_dump_json())
151
+ await self.send_peers_list()
152
+ self.is_authenticated = True
153
+ return
154
+
155
+ try:
156
+ incoming = Request.model_validate_json(text_data)
157
+ except ValidationError as error:
158
+ message = (
159
+ f"An error occurred when receiving the following message: {text_data!r}"
160
+ )
161
+ logging.error(message, error)
162
+ else:
163
+ match incoming.root:
164
+ # Broadcast all operation messages to connected peers
165
+ case OperationMessage():
166
+ await self.broadcast(text_data)
167
+
168
+ # Send peer messages to the proper peer
169
+ case PeerMessage():
170
+ await self.send_to(incoming.root.recipient, text_data)
171
+
172
+ async def send(self, text):
173
+ print(" FORWARDING TO", self.peer_id, text)
174
+ try:
175
+ await self._send({"type": "websocket.send", "text": text})
176
+ except Exception as err:
177
+ print("Error sending message:", text)
178
+ print(err)
179
+
180
+
181
+ urlpatterns = [path("ws/sync/<str:map_id>", name="ws_sync", view=sync)]
umap/sync/payloads.py ADDED
@@ -0,0 +1,49 @@
1
+ from typing import Literal, Optional, Union
2
+
3
+ from pydantic import BaseModel, Field, RootModel
4
+
5
+
6
+ class JoinRequest(BaseModel):
7
+ kind: Literal["JoinRequest"] = "JoinRequest"
8
+ token: str
9
+ peer: str
10
+ username: Optional[str] = ""
11
+
12
+
13
+ class OperationMessage(BaseModel):
14
+ """Message sent from one peer to all the others"""
15
+
16
+ kind: Literal["OperationMessage"] = "OperationMessage"
17
+ verb: Literal["upsert", "update", "delete"]
18
+ subject: Literal["map", "datalayer", "feature"]
19
+ metadata: Optional[dict] = None
20
+ key: Optional[str] = None
21
+
22
+
23
+ class PeerMessage(BaseModel):
24
+ """Message sent from a specific peer to another one"""
25
+
26
+ kind: Literal["PeerMessage"] = "PeerMessage"
27
+ sender: str
28
+ recipient: str
29
+ # The message can be whatever the peers want. It's not checked by the server.
30
+ message: dict
31
+
32
+
33
+ class Request(RootModel):
34
+ """Any message coming from the websocket should be one of these, and will be rejected otherwise."""
35
+
36
+ root: Union[PeerMessage, OperationMessage] = Field(discriminator="kind")
37
+
38
+
39
+ class JoinResponse(BaseModel):
40
+ """Server response containing the list of peers"""
41
+
42
+ kind: Literal["JoinResponse"] = "JoinResponse"
43
+ peers: dict
44
+ peer: str
45
+
46
+
47
+ class ListPeersResponse(BaseModel):
48
+ kind: Literal["ListPeersResponse"] = "ListPeersResponse"
49
+ peers: dict
@@ -2,6 +2,10 @@
2
2
 
3
3
  {% load i18n %}
4
4
 
5
+ {% block head_title %}
6
+ {% blocktranslate %}{{ current_user }}’s maps{% endblocktranslate %} - {{ SITE_DESCRIPTION }}
7
+ {% endblock head_title %}
8
+
5
9
  {% block maincontent %}
6
10
  <div class="col wide">
7
11
  <h2 class="section">
@@ -1,7 +1,10 @@
1
1
  {% extends "umap/content.html" %}
2
2
 
3
- {% load i18n %}
3
+ {% load i18n static %}
4
4
 
5
+ {% block head_title %}
6
+ {% translate "My Profile" %} - {{ SITE_DESCRIPTION }}
7
+ {% endblock head_title %}
5
8
  {% block maincontent %}
6
9
  {% include "umap/dashboard_menu.html" with selected="profile" %}
7
10
  <div class="wrapper">
@@ -28,8 +31,10 @@
28
31
  </h3>
29
32
  <ul>
30
33
  {% for name in providers %}
31
- <li>
32
- {{ name|title }}
34
+ <li class="login-grid">
35
+ {% with "umap/img/providers/"|add:name|add:".png" as path %}
36
+ <img src="{% static path %}" width="92px" height="92px" alt="{{ name }}" />
37
+ {% endwith %}
33
38
  </li>
34
39
  {% endfor %}
35
40
  </ul>
@@ -46,9 +51,7 @@
46
51
  {% for name in backends.backends %}
47
52
  {% if name not in providers %}
48
53
  <li>
49
- <a href="{% url "social:begin" name %}"
50
- class="umap-login-popup login-{{ name }}"
51
- title="{{ name|title }}"></a>
54
+ {% include "umap/components/provider.html" with name=name %}
52
55
  </li>
53
56
  {% endif %}
54
57
  {% endfor %}
@@ -2,6 +2,10 @@
2
2
 
3
3
  {% load i18n %}
4
4
 
5
+ {% block head_title %}
6
+ {% blocktranslate %}{{ current_user }}’s starred maps{% endblocktranslate %} - {{ SITE_DESCRIPTION }}
7
+ {% endblock head_title %}
8
+
5
9
  {% block maincontent %}
6
10
  <div class="col wide">
7
11
  <h2 class="section">
umap/templates/base.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <head>
6
6
  <title>
7
7
  {% block head_title %}
8
- {{ SITE_NAME }}
8
+ {{ SITE_NAME }} - {{ SITE_DESCRIPTION }}
9
9
  {% endblock head_title %}
10
10
  </title>
11
11
  <meta charset="utf-8">