umap-project 2.5.0__py3-none-any.whl → 2.6.0__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.
- umap/__init__.py +1 -1
- umap/admin.py +6 -1
- umap/context_processors.py +2 -1
- umap/decorators.py +13 -2
- umap/forms.py +26 -2
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +252 -146
- umap/locale/ca/LC_MESSAGES/django.mo +0 -0
- umap/locale/ca/LC_MESSAGES/django.po +274 -162
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +261 -150
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +299 -187
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +215 -159
- umap/locale/en/LC_MESSAGES/django.po +211 -155
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +255 -144
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +254 -198
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +347 -235
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +216 -160
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +215 -159
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +252 -146
- umap/locale/ms/LC_MESSAGES/django.mo +0 -0
- umap/locale/ms/LC_MESSAGES/django.po +252 -146
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +254 -148
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +215 -159
- umap/locale/sv/LC_MESSAGES/django.mo +0 -0
- umap/locale/sv/LC_MESSAGES/django.po +254 -143
- umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
- umap/locale/th_TH/LC_MESSAGES/django.po +125 -70
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +256 -145
- umap/migrations/0022_add_team.py +94 -0
- umap/models.py +45 -10
- umap/settings/__init__.py +2 -0
- umap/settings/base.py +9 -2
- umap/static/umap/base.css +32 -41
- umap/static/umap/content.css +19 -25
- umap/static/umap/css/icon.css +63 -37
- umap/static/umap/css/importers.css +1 -1
- umap/static/umap/css/slideshow.css +7 -5
- umap/static/umap/css/tableeditor.css +4 -3
- umap/static/umap/img/16-white.svg +1 -4
- umap/static/umap/img/16.svg +2 -6
- umap/static/umap/img/24-white.svg +4 -4
- umap/static/umap/img/24.svg +6 -6
- umap/static/umap/img/source/16-white.svg +2 -5
- umap/static/umap/img/source/16.svg +3 -7
- umap/static/umap/img/source/24-white.svg +7 -14
- umap/static/umap/img/source/24.svg +10 -17
- umap/static/umap/js/components/alerts/alert.css +20 -8
- umap/static/umap/js/modules/autocomplete.js +8 -12
- umap/static/umap/js/modules/browser.js +4 -3
- umap/static/umap/js/modules/caption.js +9 -11
- umap/static/umap/js/modules/data/features.js +993 -0
- umap/static/umap/js/modules/data/layer.js +1210 -0
- umap/static/umap/js/modules/formatter.js +12 -3
- umap/static/umap/js/modules/global.js +21 -5
- umap/static/umap/js/modules/importers/overpass.js +22 -8
- umap/static/umap/js/modules/permissions.js +280 -0
- umap/static/umap/js/{umap.icon.js → modules/rendering/icon.js} +77 -56
- umap/static/umap/js/modules/rendering/layers/base.js +105 -0
- umap/static/umap/js/modules/rendering/layers/classified.js +484 -0
- umap/static/umap/js/modules/rendering/layers/cluster.js +103 -0
- umap/static/umap/js/modules/rendering/layers/heat.js +182 -0
- umap/static/umap/js/modules/rendering/popup.js +99 -0
- umap/static/umap/js/modules/rendering/template.js +217 -0
- umap/static/umap/js/modules/rendering/ui.js +610 -0
- umap/static/umap/js/modules/rules.js +16 -3
- umap/static/umap/js/modules/schema.js +25 -1
- umap/static/umap/js/modules/share.js +66 -45
- umap/static/umap/js/modules/sync/updaters.js +9 -10
- umap/static/umap/js/modules/tableeditor.js +7 -7
- umap/static/umap/js/modules/ui/dialog.js +8 -4
- umap/static/umap/js/modules/utils.js +22 -13
- umap/static/umap/js/umap.controls.js +80 -146
- umap/static/umap/js/umap.core.js +9 -9
- umap/static/umap/js/umap.forms.js +41 -17
- umap/static/umap/js/umap.js +72 -65
- umap/static/umap/locale/am_ET.js +8 -2
- umap/static/umap/locale/am_ET.json +8 -2
- umap/static/umap/locale/ar.js +8 -2
- umap/static/umap/locale/ar.json +8 -2
- umap/static/umap/locale/ast.js +8 -2
- umap/static/umap/locale/ast.json +8 -2
- umap/static/umap/locale/bg.js +8 -2
- umap/static/umap/locale/bg.json +8 -2
- umap/static/umap/locale/br.js +42 -36
- umap/static/umap/locale/br.json +42 -36
- umap/static/umap/locale/ca.js +67 -61
- umap/static/umap/locale/ca.json +67 -61
- umap/static/umap/locale/cs_CZ.js +8 -2
- umap/static/umap/locale/cs_CZ.json +8 -2
- umap/static/umap/locale/da.js +8 -2
- umap/static/umap/locale/da.json +8 -2
- umap/static/umap/locale/de.js +143 -137
- umap/static/umap/locale/de.json +143 -137
- umap/static/umap/locale/el.js +54 -48
- umap/static/umap/locale/el.json +54 -48
- umap/static/umap/locale/en.js +10 -2
- umap/static/umap/locale/en.json +10 -2
- umap/static/umap/locale/en_US.json +8 -2
- umap/static/umap/locale/es.js +8 -2
- umap/static/umap/locale/es.json +8 -2
- umap/static/umap/locale/et.js +8 -2
- umap/static/umap/locale/et.json +8 -2
- umap/static/umap/locale/eu.js +346 -338
- umap/static/umap/locale/eu.json +346 -338
- umap/static/umap/locale/fa_IR.js +415 -407
- umap/static/umap/locale/fa_IR.json +415 -407
- umap/static/umap/locale/fi.js +8 -2
- umap/static/umap/locale/fi.json +8 -2
- umap/static/umap/locale/fr.js +11 -3
- umap/static/umap/locale/fr.json +11 -3
- umap/static/umap/locale/gl.js +8 -2
- umap/static/umap/locale/gl.json +8 -2
- umap/static/umap/locale/he.js +8 -2
- umap/static/umap/locale/he.json +8 -2
- umap/static/umap/locale/hr.js +8 -2
- umap/static/umap/locale/hr.json +8 -2
- umap/static/umap/locale/hu.js +31 -23
- umap/static/umap/locale/hu.json +31 -23
- umap/static/umap/locale/id.js +8 -2
- umap/static/umap/locale/id.json +8 -2
- umap/static/umap/locale/is.js +8 -2
- umap/static/umap/locale/is.json +8 -2
- umap/static/umap/locale/it.js +8 -2
- umap/static/umap/locale/it.json +8 -2
- umap/static/umap/locale/ja.js +8 -2
- umap/static/umap/locale/ja.json +8 -2
- umap/static/umap/locale/ko.js +8 -2
- umap/static/umap/locale/ko.json +8 -2
- umap/static/umap/locale/lt.js +8 -2
- umap/static/umap/locale/lt.json +8 -2
- umap/static/umap/locale/ms.js +8 -2
- umap/static/umap/locale/ms.json +8 -2
- umap/static/umap/locale/nl.js +8 -2
- umap/static/umap/locale/nl.json +8 -2
- umap/static/umap/locale/no.js +8 -2
- umap/static/umap/locale/no.json +8 -2
- umap/static/umap/locale/pl.js +54 -48
- umap/static/umap/locale/pl.json +54 -48
- umap/static/umap/locale/pl_PL.json +8 -2
- umap/static/umap/locale/pt.js +24 -18
- umap/static/umap/locale/pt.json +24 -18
- umap/static/umap/locale/pt_BR.js +8 -2
- umap/static/umap/locale/pt_BR.json +8 -2
- umap/static/umap/locale/pt_PT.js +214 -208
- umap/static/umap/locale/pt_PT.json +214 -208
- umap/static/umap/locale/ro.js +8 -2
- umap/static/umap/locale/ro.json +8 -2
- umap/static/umap/locale/ru.js +8 -2
- umap/static/umap/locale/ru.json +8 -2
- umap/static/umap/locale/sk_SK.js +8 -2
- umap/static/umap/locale/sk_SK.json +8 -2
- umap/static/umap/locale/sl.js +8 -2
- umap/static/umap/locale/sl.json +8 -2
- umap/static/umap/locale/sr.js +8 -2
- umap/static/umap/locale/sr.json +8 -2
- umap/static/umap/locale/sv.js +8 -2
- umap/static/umap/locale/sv.json +8 -2
- umap/static/umap/locale/th_TH.js +33 -27
- umap/static/umap/locale/th_TH.json +33 -27
- umap/static/umap/locale/tr.js +8 -2
- umap/static/umap/locale/tr.json +8 -2
- umap/static/umap/locale/uk_UA.js +8 -2
- umap/static/umap/locale/uk_UA.json +8 -2
- umap/static/umap/locale/vi.js +8 -2
- umap/static/umap/locale/vi.json +8 -2
- umap/static/umap/locale/vi_VN.json +8 -2
- umap/static/umap/locale/zh.js +8 -2
- umap/static/umap/locale/zh.json +8 -2
- umap/static/umap/locale/zh_CN.json +8 -2
- umap/static/umap/locale/zh_TW.Big5.json +8 -2
- umap/static/umap/locale/zh_TW.js +102 -96
- umap/static/umap/locale/zh_TW.json +102 -96
- umap/static/umap/map.css +111 -108
- umap/static/umap/nav.css +19 -10
- umap/static/umap/unittests/utils.js +230 -107
- umap/static/umap/vars.css +1 -0
- umap/static/umap/vendors/csv2geojson/csv2geojson.js +62 -40
- umap/static/umap/vendors/editable/Leaflet.Editable.js +2079 -1937
- umap/storage.py +4 -3
- umap/templates/404.html +5 -1
- umap/templates/500.html +3 -1
- umap/templates/auth/user_detail.html +8 -2
- umap/templates/auth/user_form.html +19 -10
- umap/templates/auth/user_stars.html +8 -2
- umap/templates/base.html +1 -0
- umap/templates/registration/login.html +18 -3
- umap/templates/umap/about.html +1 -0
- umap/templates/umap/about_summary.html +22 -7
- umap/templates/umap/components/alerts/alert.html +42 -21
- umap/templates/umap/content.html +2 -0
- umap/templates/umap/content_footer.html +7 -3
- umap/templates/umap/css.html +1 -0
- umap/templates/umap/dashboard_menu.html +15 -0
- umap/templates/umap/home.html +14 -4
- umap/templates/umap/js.html +4 -9
- umap/templates/umap/login_popup_end.html +10 -4
- umap/templates/umap/map_detail.html +8 -2
- umap/templates/umap/map_fragment.html +3 -1
- umap/templates/umap/map_init.html +2 -1
- umap/templates/umap/map_list.html +6 -3
- umap/templates/umap/map_table.html +36 -12
- umap/templates/umap/messages.html +0 -1
- umap/templates/umap/navigation.html +2 -1
- umap/templates/umap/password_change.html +5 -1
- umap/templates/umap/password_change_done.html +8 -2
- umap/templates/umap/search.html +8 -2
- umap/templates/umap/search_bar.html +1 -0
- umap/templates/umap/team_confirm_delete.html +19 -0
- umap/templates/umap/team_detail.html +27 -0
- umap/templates/umap/team_form.html +60 -0
- umap/templates/umap/user_dashboard.html +7 -9
- umap/templates/umap/user_teams.html +51 -0
- umap/tests/base.py +8 -1
- umap/tests/conftest.py +6 -0
- umap/tests/fixtures/test_circles_layer.geojson +219 -0
- umap/tests/fixtures/test_upload_georss.xml +20 -0
- umap/tests/integration/conftest.py +18 -4
- umap/tests/integration/helpers.py +12 -0
- umap/tests/integration/test_anonymous_owned_map.py +23 -0
- umap/tests/integration/test_basics.py +29 -0
- umap/tests/integration/test_browser.py +20 -0
- umap/tests/integration/test_caption.py +20 -0
- umap/tests/integration/test_circles_layer.py +69 -0
- umap/tests/integration/test_conditional_rules.py +102 -17
- umap/tests/integration/test_draw_polygon.py +138 -13
- umap/tests/integration/test_draw_polyline.py +8 -18
- umap/tests/integration/test_edit_datalayer.py +3 -3
- umap/tests/integration/test_import.py +124 -5
- umap/tests/integration/test_owned_map.py +21 -13
- umap/tests/integration/test_querystring.py +7 -0
- umap/tests/integration/test_team.py +47 -0
- umap/tests/integration/test_tilelayer.py +19 -2
- umap/tests/integration/test_view_marker.py +28 -1
- umap/tests/integration/test_websocket_sync.py +5 -5
- umap/tests/test_datalayer.py +32 -7
- umap/tests/test_datalayer_views.py +1 -1
- umap/tests/test_map.py +30 -4
- umap/tests/test_map_views.py +2 -2
- umap/tests/test_statics.py +40 -0
- umap/tests/test_team_views.py +131 -0
- umap/tests/test_views.py +15 -1
- umap/urls.py +23 -13
- umap/views.py +116 -10
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/METADATA +14 -14
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/RECORD +260 -253
- umap/static/umap/js/umap.datalayer.permissions.js +0 -70
- umap/static/umap/js/umap.features.js +0 -1290
- umap/static/umap/js/umap.layer.js +0 -1837
- umap/static/umap/js/umap.permissions.js +0 -208
- umap/static/umap/js/umap.popup.js +0 -341
- umap/static/umap/test/TableEditor.js +0 -104
- umap/static/umap/vendors/leaflet/leaflet-src.js +0 -14512
- umap/static/umap/vendors/leaflet/leaflet-src.js.map +0 -1
- umap/static/umap/vendors/leaflet/leaflet.js +0 -6
- umap/static/umap/vendors/leaflet/leaflet.js.map +0 -1
- umap/static/umap/vendors/markercluster/WhereAreTheJavascriptFiles.txt +0 -5
- umap/static/umap/vendors/markercluster/leaflet.markercluster-src.js +0 -2718
- umap/static/umap/vendors/markercluster/leaflet.markercluster-src.js.map +0 -1
- umap/static/umap/vendors/toolbar/leaflet.toolbar-src.css +0 -117
- umap/static/umap/vendors/toolbar/leaflet.toolbar-src.js +0 -365
- umap/tests/integration/test_statics.py +0 -47
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/WHEEL +0 -0
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.5.0.dist-info → umap_project-2.6.0.dist-info}/licenses/LICENSE +0 -0
umap/tests/test_map_views.py
CHANGED
|
@@ -210,7 +210,7 @@ def test_user_not_allowed_should_not_clone_map(client, map, user, settings):
|
|
|
210
210
|
|
|
211
211
|
def test_clone_should_set_cloner_as_owner(client, map, user):
|
|
212
212
|
url = reverse("map_clone", kwargs={"map_id": map.pk})
|
|
213
|
-
map.edit_status = map.
|
|
213
|
+
map.edit_status = map.COLLABORATORS
|
|
214
214
|
map.editors.add(user)
|
|
215
215
|
map.save()
|
|
216
216
|
client.login(username=user.username, password="123123")
|
|
@@ -330,7 +330,7 @@ def test_only_owner_can_delete(client, map, user):
|
|
|
330
330
|
|
|
331
331
|
def test_map_editors_do_not_see_owner_change_input(client, map, user):
|
|
332
332
|
map.editors.add(user)
|
|
333
|
-
map.edit_status = map.
|
|
333
|
+
map.edit_status = map.COLLABORATORS
|
|
334
334
|
map.save()
|
|
335
335
|
url = reverse("map_update_permissions", kwargs={"map_id": map.pk})
|
|
336
336
|
client.login(username=user.username, password="123123")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import shutil
|
|
3
|
+
import tempfile
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from django.core.management import call_command
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def staticfiles(settings):
|
|
13
|
+
static_root = tempfile.mkdtemp(prefix="test_static")
|
|
14
|
+
settings.STATIC_ROOT = static_root
|
|
15
|
+
# Make sure settings are properly reset after the test
|
|
16
|
+
settings.STORAGES = deepcopy(settings.STORAGES)
|
|
17
|
+
settings.STORAGES["staticfiles"]["BACKEND"] = (
|
|
18
|
+
"umap.storage.UmapManifestStaticFilesStorage"
|
|
19
|
+
)
|
|
20
|
+
try:
|
|
21
|
+
call_command("collectstatic", "--noinput")
|
|
22
|
+
yield
|
|
23
|
+
finally:
|
|
24
|
+
shutil.rmtree(static_root)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_collectstatic_ran_successfully_with_hashes(settings, staticfiles):
|
|
28
|
+
static_root = settings.STATIC_ROOT
|
|
29
|
+
manifest = Path(static_root) / "staticfiles.json"
|
|
30
|
+
assert manifest.exists()
|
|
31
|
+
json_manifest = json.loads(manifest.read_text())
|
|
32
|
+
assert "hash" in json_manifest.keys()
|
|
33
|
+
assert "umap/base.css" in json_manifest["paths"]
|
|
34
|
+
# Hash + the dot ("umap/base.<hash>.css").
|
|
35
|
+
md5_hash_lenght = 12 + 1
|
|
36
|
+
# The value of the manifest must contain the hash (length).
|
|
37
|
+
assert (
|
|
38
|
+
len(json_manifest["paths"]["umap/base.css"])
|
|
39
|
+
== len("umap/base.css") + md5_hash_lenght
|
|
40
|
+
)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.urls import reverse
|
|
3
|
+
|
|
4
|
+
from umap.models import Team
|
|
5
|
+
|
|
6
|
+
pytestmark = pytest.mark.django_db
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_can_see_team_maps(client, map, team):
|
|
10
|
+
map.team = team
|
|
11
|
+
map.save()
|
|
12
|
+
url = reverse("team_maps", args=(team.pk,))
|
|
13
|
+
response = client.get(url)
|
|
14
|
+
assert response.status_code == 200
|
|
15
|
+
assert map.name in response.content.decode()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_user_can_see_their_teams(client, team, user):
|
|
19
|
+
user.teams.add(team)
|
|
20
|
+
user.save()
|
|
21
|
+
url = reverse("user_teams")
|
|
22
|
+
client.login(username=user.username, password="123123")
|
|
23
|
+
response = client.get(url)
|
|
24
|
+
assert response.status_code == 200
|
|
25
|
+
assert team.name in response.content.decode()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_can_create_a_team(client, user):
|
|
29
|
+
assert not Team.objects.count()
|
|
30
|
+
url = reverse("team_new")
|
|
31
|
+
client.login(username=user.username, password="123123")
|
|
32
|
+
response = client.post(url, {"name": "my new team", "members": [user.pk]})
|
|
33
|
+
assert response.status_code == 302
|
|
34
|
+
assert response["Location"] == "/en/me/teams"
|
|
35
|
+
assert Team.objects.count() == 1
|
|
36
|
+
team = Team.objects.first()
|
|
37
|
+
assert team.name == "my new team"
|
|
38
|
+
assert team in user.teams.all()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_can_edit_a_team_name(client, user, team):
|
|
42
|
+
user.teams.add(team)
|
|
43
|
+
user.save()
|
|
44
|
+
assert Team.objects.count() == 1
|
|
45
|
+
url = reverse("team_update", args=(team.pk,))
|
|
46
|
+
client.login(username=user.username, password="123123")
|
|
47
|
+
response = client.post(url, {"name": "my new team", "members": [user.pk]})
|
|
48
|
+
assert response.status_code == 302
|
|
49
|
+
assert response["Location"] == "/en/me/teams"
|
|
50
|
+
assert Team.objects.count() == 1
|
|
51
|
+
modified = Team.objects.first()
|
|
52
|
+
assert modified.name == "my new team"
|
|
53
|
+
assert modified in user.teams.all()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_can_add_user_to_team(client, user, user2, team):
|
|
57
|
+
user.teams.add(team)
|
|
58
|
+
user.save()
|
|
59
|
+
assert Team.objects.count() == 1
|
|
60
|
+
url = reverse("team_update", args=(team.pk,))
|
|
61
|
+
client.login(username=user.username, password="123123")
|
|
62
|
+
response = client.post(url, {"name": team.name, "members": [user.pk, user2.pk]})
|
|
63
|
+
assert response.status_code == 302
|
|
64
|
+
assert response["Location"] == "/en/me/teams"
|
|
65
|
+
assert Team.objects.count() == 1
|
|
66
|
+
modified = Team.objects.first()
|
|
67
|
+
assert user in modified.users.all()
|
|
68
|
+
assert user2 in modified.users.all()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_can_remove_user_from_team(client, user, user2, team):
|
|
72
|
+
user.teams.add(team)
|
|
73
|
+
user.save()
|
|
74
|
+
user2.teams.add(team)
|
|
75
|
+
user2.save()
|
|
76
|
+
assert Team.objects.count() == 1
|
|
77
|
+
url = reverse("team_update", args=(team.pk,))
|
|
78
|
+
client.login(username=user.username, password="123123")
|
|
79
|
+
response = client.post(url, {"name": team.name, "members": [user.pk]})
|
|
80
|
+
assert response.status_code == 302
|
|
81
|
+
assert response["Location"] == "/en/me/teams"
|
|
82
|
+
assert Team.objects.count() == 1
|
|
83
|
+
modified = Team.objects.first()
|
|
84
|
+
assert user in modified.users.all()
|
|
85
|
+
assert user2 not in modified.users.all()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_cannot_edit_a_team_if_not_member(client, user, user2, team):
|
|
89
|
+
user.teams.add(team)
|
|
90
|
+
user.save()
|
|
91
|
+
assert Team.objects.count() == 1
|
|
92
|
+
url = reverse("team_update", args=(team.pk,))
|
|
93
|
+
client.login(username=user2.username, password="456456")
|
|
94
|
+
response = client.post(url, {"name": "my new team", "members": [user.pk]})
|
|
95
|
+
assert response.status_code == 403
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_can_delete_a_team(client, user, team):
|
|
99
|
+
user.teams.add(team)
|
|
100
|
+
user.save()
|
|
101
|
+
assert Team.objects.count() == 1
|
|
102
|
+
url = reverse("team_delete", args=(team.pk,))
|
|
103
|
+
client.login(username=user.username, password="123123")
|
|
104
|
+
response = client.post(url)
|
|
105
|
+
assert response.status_code == 302
|
|
106
|
+
assert response["Location"] == "/en/me/teams"
|
|
107
|
+
assert Team.objects.count() == 0
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_cannot_delete_a_team_if_not_member(client, user, user2, team):
|
|
111
|
+
user.teams.add(team)
|
|
112
|
+
user.save()
|
|
113
|
+
assert Team.objects.count() == 1
|
|
114
|
+
url = reverse("team_delete", args=(team.pk,))
|
|
115
|
+
client.login(username=user2.username, password="456456")
|
|
116
|
+
response = client.post(url)
|
|
117
|
+
assert response.status_code == 403
|
|
118
|
+
assert Team.objects.count() == 1
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_cannot_delete_a_team_if_more_than_one_member(client, user, user2, team):
|
|
122
|
+
user.teams.add(team)
|
|
123
|
+
user.save()
|
|
124
|
+
user2.teams.add(team)
|
|
125
|
+
user2.save()
|
|
126
|
+
assert Team.objects.count() == 1
|
|
127
|
+
url = reverse("team_delete", args=(team.pk,))
|
|
128
|
+
client.login(username=user.username, password="123123")
|
|
129
|
+
response = client.post(url)
|
|
130
|
+
assert response.status_code == 400
|
|
131
|
+
assert Team.objects.count() == 1
|
umap/tests/test_views.py
CHANGED
|
@@ -288,6 +288,20 @@ def test_user_dashboard_display_user_maps(client, map):
|
|
|
288
288
|
assert "Owner only" in body
|
|
289
289
|
|
|
290
290
|
|
|
291
|
+
@pytest.mark.django_db
|
|
292
|
+
def test_user_dashboard_display_user_team_maps(client, map, team, user):
|
|
293
|
+
user.teams.add(team)
|
|
294
|
+
user.save()
|
|
295
|
+
map.team = team
|
|
296
|
+
map.save()
|
|
297
|
+
client.login(username=user.username, password="123123")
|
|
298
|
+
response = client.get(reverse("user_dashboard"))
|
|
299
|
+
assert response.status_code == 200
|
|
300
|
+
body = response.content.decode()
|
|
301
|
+
assert map.name in body
|
|
302
|
+
assert map.get_absolute_url() in body
|
|
303
|
+
|
|
304
|
+
|
|
291
305
|
@pytest.mark.django_db
|
|
292
306
|
def test_user_dashboard_display_user_maps_distinct(client, map):
|
|
293
307
|
# cf https://github.com/umap-project/umap/issues/1325
|
|
@@ -474,7 +488,7 @@ def test_websocket_token_returns_a_valid_token_when_authorized(client, user, map
|
|
|
474
488
|
|
|
475
489
|
@pytest.mark.django_db
|
|
476
490
|
def test_websocket_token_is_generated_for_editors(client, user, user2, map):
|
|
477
|
-
map.edit_status = Map.
|
|
491
|
+
map.edit_status = Map.COLLABORATORS
|
|
478
492
|
map.editors.add(user2)
|
|
479
493
|
map.save()
|
|
480
494
|
|
umap/urls.py
CHANGED
|
@@ -16,6 +16,7 @@ from .decorators import (
|
|
|
16
16
|
can_edit_map,
|
|
17
17
|
can_view_map,
|
|
18
18
|
login_required_if_not_anonymous_allowed,
|
|
19
|
+
team_members_only,
|
|
19
20
|
)
|
|
20
21
|
from .utils import decorated_patterns
|
|
21
22
|
|
|
@@ -24,6 +25,10 @@ admin.autodiscover()
|
|
|
24
25
|
urlpatterns = [
|
|
25
26
|
re_path(r"^admin/", admin.site.urls),
|
|
26
27
|
re_path("", include("social_django.urls", namespace="social")),
|
|
28
|
+
re_path(
|
|
29
|
+
r"^agnocomplete/",
|
|
30
|
+
include(("agnocomplete.urls", "agnocomplete"), namespace="agnocomplete"),
|
|
31
|
+
),
|
|
27
32
|
re_path(r"^m/(?P<pk>\d+)/$", views.MapShortUrl.as_view(), name="map_short_url"),
|
|
28
33
|
re_path(r"^ajax-proxy/$", cache_page(180)(views.ajax_proxy), name="ajax-proxy"),
|
|
29
34
|
re_path(
|
|
@@ -39,7 +44,6 @@ urlpatterns = [
|
|
|
39
44
|
name="password_change_done",
|
|
40
45
|
),
|
|
41
46
|
re_path(r"^i18n/", include("django.conf.urls.i18n")),
|
|
42
|
-
re_path(r"^agnocomplete/", include("agnocomplete.urls")),
|
|
43
47
|
re_path(r"^map/oembed/", views.MapOEmbed.as_view(), name="map_oembed"),
|
|
44
48
|
re_path(
|
|
45
49
|
r"^map/(?P<map_id>\d+)/download/",
|
|
@@ -96,12 +100,12 @@ i18n_urls += decorated_patterns(
|
|
|
96
100
|
)
|
|
97
101
|
i18n_urls += decorated_patterns(
|
|
98
102
|
[ensure_csrf_cookie],
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
path("map/", views.MapPreview.as_view(), name="map_preview"),
|
|
104
|
+
path("map/new/", views.MapNew.as_view(), name="map_new"),
|
|
101
105
|
)
|
|
102
106
|
i18n_urls += decorated_patterns(
|
|
103
107
|
[login_required_if_not_anonymous_allowed, never_cache],
|
|
104
|
-
|
|
108
|
+
path("map/create/", views.MapCreate.as_view(), name="map_create"),
|
|
105
109
|
)
|
|
106
110
|
i18n_urls += decorated_patterns(
|
|
107
111
|
[login_required],
|
|
@@ -110,9 +114,16 @@ i18n_urls += decorated_patterns(
|
|
|
110
114
|
views.ToggleMapStarStatus.as_view(),
|
|
111
115
|
name="map_star",
|
|
112
116
|
),
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
path("me", views.user_dashboard, name="user_dashboard"),
|
|
118
|
+
path("me/profile", views.user_profile, name="user_profile"),
|
|
119
|
+
path("me/download", views.user_download, name="user_download"),
|
|
120
|
+
path("me/teams", views.UserTeams.as_view(), name="user_teams"),
|
|
121
|
+
path("team/create/", views.TeamNew.as_view(), name="team_new"),
|
|
122
|
+
)
|
|
123
|
+
i18n_urls += decorated_patterns(
|
|
124
|
+
[login_required, team_members_only],
|
|
125
|
+
path("team/<int:pk>/edit/", views.TeamUpdate.as_view(), name="team_update"),
|
|
126
|
+
path("team/<int:pk>/delete/", views.TeamDelete.as_view(), name="team_delete"),
|
|
116
127
|
)
|
|
117
128
|
map_urls = [
|
|
118
129
|
re_path(
|
|
@@ -179,14 +190,13 @@ datalayer_urls = [
|
|
|
179
190
|
i18n_urls += decorated_patterns([can_edit_map, never_cache], *map_urls)
|
|
180
191
|
i18n_urls += decorated_patterns([never_cache], *datalayer_urls)
|
|
181
192
|
urlpatterns += i18n_patterns(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
),
|
|
186
|
-
re_path(r"^search/$", views.search, name="search"),
|
|
187
|
-
re_path(r"^about/$", views.about, name="about"),
|
|
193
|
+
path("", views.home, name="home"),
|
|
194
|
+
path("showcase/", cache_page(24 * 60 * 60)(views.showcase), name="maps_showcase"),
|
|
195
|
+
path("search/", views.search, name="search"),
|
|
196
|
+
path("about/", views.about, name="about"),
|
|
188
197
|
re_path(r"^user/(?P<identifier>.+)/stars/$", views.user_stars, name="user_stars"),
|
|
189
198
|
re_path(r"^user/(?P<identifier>.+)/$", views.user_maps, name="user_maps"),
|
|
199
|
+
path("team/<int:pk>/", views.TeamMaps.as_view(), name="team_maps"),
|
|
190
200
|
re_path(r"", include(i18n_urls)),
|
|
191
201
|
)
|
|
192
202
|
urlpatterns += (
|
umap/views.py
CHANGED
|
@@ -62,10 +62,11 @@ from .forms import (
|
|
|
62
62
|
FlatErrorList,
|
|
63
63
|
MapSettingsForm,
|
|
64
64
|
SendLinkForm,
|
|
65
|
+
TeamForm,
|
|
65
66
|
UpdateMapPermissionsForm,
|
|
66
67
|
UserProfileForm,
|
|
67
68
|
)
|
|
68
|
-
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
|
69
|
+
from .models import DataLayer, Licence, Map, Pictogram, Star, Team, TileLayer
|
|
69
70
|
from .utils import (
|
|
70
71
|
ConflictError,
|
|
71
72
|
_urls_for_js,
|
|
@@ -188,6 +189,70 @@ class About(Home):
|
|
|
188
189
|
about = About.as_view()
|
|
189
190
|
|
|
190
191
|
|
|
192
|
+
class TeamNew(CreateView):
|
|
193
|
+
model = Team
|
|
194
|
+
fields = ["name", "description"]
|
|
195
|
+
success_url = reverse_lazy("user_teams")
|
|
196
|
+
|
|
197
|
+
def form_valid(self, form):
|
|
198
|
+
response = super().form_valid(form)
|
|
199
|
+
self.request.user.teams.add(self.object)
|
|
200
|
+
self.request.user.save()
|
|
201
|
+
return response
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class TeamUpdate(UpdateView):
|
|
205
|
+
model = Team
|
|
206
|
+
form_class = TeamForm
|
|
207
|
+
success_url = reverse_lazy("user_teams")
|
|
208
|
+
|
|
209
|
+
def get_initial(self):
|
|
210
|
+
initial = super().get_initial()
|
|
211
|
+
initial["members"] = self.object.users.all()
|
|
212
|
+
return initial
|
|
213
|
+
|
|
214
|
+
def form_valid(self, form):
|
|
215
|
+
actual = self.object.users.all()
|
|
216
|
+
wanted = form.cleaned_data["members"]
|
|
217
|
+
for user in wanted:
|
|
218
|
+
if user not in actual:
|
|
219
|
+
user.teams.add(self.object)
|
|
220
|
+
user.save()
|
|
221
|
+
for user in actual:
|
|
222
|
+
if user not in wanted:
|
|
223
|
+
user.teams.remove(self.object)
|
|
224
|
+
user.save()
|
|
225
|
+
return super().form_valid(form)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class TeamDelete(DeleteView):
|
|
229
|
+
model = Team
|
|
230
|
+
success_url = reverse_lazy("user_teams")
|
|
231
|
+
|
|
232
|
+
def form_valid(self, form):
|
|
233
|
+
if self.object.users.count() > 1:
|
|
234
|
+
return HttpResponseBadRequest(
|
|
235
|
+
_("Cannot delete a team with more than one member")
|
|
236
|
+
)
|
|
237
|
+
messages.info(
|
|
238
|
+
self.request,
|
|
239
|
+
_("Team “%(name)s” has been deleted") % {"name": self.object.name},
|
|
240
|
+
)
|
|
241
|
+
return super().form_valid(form)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class UserTeams(DetailView):
|
|
245
|
+
model = User
|
|
246
|
+
template_name = "umap/user_teams.html"
|
|
247
|
+
|
|
248
|
+
def get_object(self):
|
|
249
|
+
return self.get_queryset().get(pk=self.request.user.pk)
|
|
250
|
+
|
|
251
|
+
def get_context_data(self, **kwargs):
|
|
252
|
+
kwargs.update({"teams": self.object.teams.all()})
|
|
253
|
+
return super().get_context_data(**kwargs)
|
|
254
|
+
|
|
255
|
+
|
|
191
256
|
class UserProfile(UpdateView):
|
|
192
257
|
model = User
|
|
193
258
|
form_class = UserProfileForm
|
|
@@ -247,6 +312,21 @@ class UserStars(UserMaps):
|
|
|
247
312
|
user_stars = UserStars.as_view()
|
|
248
313
|
|
|
249
314
|
|
|
315
|
+
class TeamMaps(PaginatorMixin, DetailView):
|
|
316
|
+
model = Team
|
|
317
|
+
list_template_name = "umap/map_list.html"
|
|
318
|
+
context_object_name = "current_team"
|
|
319
|
+
|
|
320
|
+
def get_maps(self):
|
|
321
|
+
return Map.public.filter(team=self.object).order_by("-modified_at")
|
|
322
|
+
|
|
323
|
+
def get_context_data(self, **kwargs):
|
|
324
|
+
kwargs.update(
|
|
325
|
+
{"maps": self.paginate(self.get_maps(), settings.UMAP_MAPS_PER_PAGE)}
|
|
326
|
+
)
|
|
327
|
+
return super().get_context_data(**kwargs)
|
|
328
|
+
|
|
329
|
+
|
|
250
330
|
class SearchMixin:
|
|
251
331
|
def get_search_queryset(self, **kwargs):
|
|
252
332
|
q = self.request.GET.get("q")
|
|
@@ -293,7 +373,12 @@ class UserDashboard(PaginatorMixin, DetailView, SearchMixin):
|
|
|
293
373
|
|
|
294
374
|
def get_maps(self):
|
|
295
375
|
qs = self.get_search_queryset() or Map.objects.all()
|
|
296
|
-
|
|
376
|
+
teams = self.object.teams.all()
|
|
377
|
+
qs = (
|
|
378
|
+
qs.filter(owner=self.object)
|
|
379
|
+
.union(qs.filter(editors=self.object))
|
|
380
|
+
.union(qs.filter(team__in=teams))
|
|
381
|
+
)
|
|
297
382
|
return qs.order_by("-modified_at")
|
|
298
383
|
|
|
299
384
|
def get_context_data(self, **kwargs):
|
|
@@ -456,6 +541,23 @@ def simple_json_response(**kwargs):
|
|
|
456
541
|
# ############## #
|
|
457
542
|
|
|
458
543
|
|
|
544
|
+
class SessionMixin:
|
|
545
|
+
def get_user_data(self):
|
|
546
|
+
data = {}
|
|
547
|
+
user = self.request.user
|
|
548
|
+
if hasattr(self, "object"):
|
|
549
|
+
data["is_owner"] = self.object.is_owner(user, self.request)
|
|
550
|
+
if user.is_anonymous:
|
|
551
|
+
return data
|
|
552
|
+
return {
|
|
553
|
+
"id": user.pk,
|
|
554
|
+
"name": str(self.request.user),
|
|
555
|
+
"url": reverse("user_dashboard"),
|
|
556
|
+
"teams": [team.get_metadata() for team in user.teams.all()],
|
|
557
|
+
**data,
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
459
561
|
class FormLessEditMixin:
|
|
460
562
|
http_method_names = [
|
|
461
563
|
"post",
|
|
@@ -470,7 +572,7 @@ class FormLessEditMixin:
|
|
|
470
572
|
return self.get_form_class()(**kwargs)
|
|
471
573
|
|
|
472
574
|
|
|
473
|
-
class MapDetailMixin:
|
|
575
|
+
class MapDetailMixin(SessionMixin):
|
|
474
576
|
model = Map
|
|
475
577
|
pk_url_kwarg = "map_id"
|
|
476
578
|
|
|
@@ -522,12 +624,7 @@ class MapDetailMixin:
|
|
|
522
624
|
if self.get_short_url():
|
|
523
625
|
properties["shortUrl"] = self.get_short_url()
|
|
524
626
|
|
|
525
|
-
|
|
526
|
-
properties["user"] = {
|
|
527
|
-
"id": user.pk,
|
|
528
|
-
"name": str(user),
|
|
529
|
-
"url": reverse("user_dashboard"),
|
|
530
|
-
}
|
|
627
|
+
properties["user"] = self.get_user_data()
|
|
531
628
|
return properties
|
|
532
629
|
|
|
533
630
|
def get_context_data(self, **kwargs):
|
|
@@ -595,6 +692,8 @@ class PermissionsMixin:
|
|
|
595
692
|
{"id": editor.pk, "name": str(editor)}
|
|
596
693
|
for editor in self.object.editors.all()
|
|
597
694
|
]
|
|
695
|
+
if self.object.team:
|
|
696
|
+
permissions["team"] = self.object.team.get_metadata()
|
|
598
697
|
if not self.object.owner and self.object.is_anonymous_owner(self.request):
|
|
599
698
|
permissions["anonymous_edit_url"] = self.object.get_anonymous_edit_url()
|
|
600
699
|
return permissions
|
|
@@ -659,6 +758,12 @@ class MapView(MapDetailMixin, PermissionsMixin, DetailView):
|
|
|
659
758
|
map_settings["properties"] = {}
|
|
660
759
|
map_settings["properties"]["name"] = self.object.name
|
|
661
760
|
map_settings["properties"]["permissions"] = self.get_permissions()
|
|
761
|
+
author = self.object.get_author()
|
|
762
|
+
if author:
|
|
763
|
+
map_settings["properties"]["author"] = {
|
|
764
|
+
"name": str(author),
|
|
765
|
+
"url": author.get_url(),
|
|
766
|
+
}
|
|
662
767
|
return map_settings
|
|
663
768
|
|
|
664
769
|
def is_starred(self):
|
|
@@ -755,7 +860,7 @@ class MapPreview(MapDetailMixin, TemplateView):
|
|
|
755
860
|
return properties
|
|
756
861
|
|
|
757
862
|
|
|
758
|
-
class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView):
|
|
863
|
+
class MapCreate(FormLessEditMixin, PermissionsMixin, SessionMixin, CreateView):
|
|
759
864
|
model = Map
|
|
760
865
|
form_class = MapSettingsForm
|
|
761
866
|
|
|
@@ -772,6 +877,7 @@ class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView):
|
|
|
772
877
|
id=self.object.pk,
|
|
773
878
|
url=self.object.get_absolute_url(),
|
|
774
879
|
permissions=permissions,
|
|
880
|
+
user=self.get_user_data(),
|
|
775
881
|
)
|
|
776
882
|
if not self.request.user.is_authenticated:
|
|
777
883
|
key, value = self.object.signed_cookie_elements
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: umap-project
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Create maps with OpenStreetMap layers in a minute and embed them in your site.
|
|
5
5
|
Author-email: Yohan Boniface <yb@enix.org>
|
|
6
6
|
Maintainer-email: David Larlet <david@larlet.fr>
|
|
@@ -19,35 +19,35 @@ Requires-Python: >=3.10
|
|
|
19
19
|
Requires-Dist: django-agnocomplete==2.2.0
|
|
20
20
|
Requires-Dist: django-environ==0.11.2
|
|
21
21
|
Requires-Dist: django-probes==1.7.0
|
|
22
|
-
Requires-Dist: django==5.
|
|
22
|
+
Requires-Dist: django==5.1.1
|
|
23
23
|
Requires-Dist: pillow==10.4.0
|
|
24
24
|
Requires-Dist: psycopg==3.2.1
|
|
25
|
-
Requires-Dist: pydantic==2.
|
|
25
|
+
Requires-Dist: pydantic==2.9.1
|
|
26
26
|
Requires-Dist: rcssmin==1.1.2
|
|
27
27
|
Requires-Dist: requests==2.32.3
|
|
28
28
|
Requires-Dist: rjsmin==1.2.2
|
|
29
|
-
Requires-Dist: social-auth-app-django==5.4.
|
|
29
|
+
Requires-Dist: social-auth-app-django==5.4.2
|
|
30
30
|
Requires-Dist: social-auth-core==4.5.4
|
|
31
|
-
Requires-Dist: websockets==
|
|
31
|
+
Requires-Dist: websockets==13.0.1
|
|
32
32
|
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: djlint==1.
|
|
33
|
+
Requires-Dist: djlint==1.35.2; extra == 'dev'
|
|
34
34
|
Requires-Dist: hatch==1.12.0; extra == 'dev'
|
|
35
35
|
Requires-Dist: isort==5.13.2; extra == 'dev'
|
|
36
|
-
Requires-Dist: mkdocs-material==9.5.
|
|
36
|
+
Requires-Dist: mkdocs-material==9.5.34; extra == 'dev'
|
|
37
37
|
Requires-Dist: mkdocs-static-i18n==1.2.3; extra == 'dev'
|
|
38
|
-
Requires-Dist: mkdocs==1.6.
|
|
39
|
-
Requires-Dist: pymdown-extensions==10.
|
|
40
|
-
Requires-Dist: ruff==0.
|
|
38
|
+
Requires-Dist: mkdocs==1.6.1; extra == 'dev'
|
|
39
|
+
Requires-Dist: pymdown-extensions==10.9; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff==0.6.4; extra == 'dev'
|
|
41
41
|
Requires-Dist: vermin==1.6.0; extra == 'dev'
|
|
42
42
|
Provides-Extra: docker
|
|
43
43
|
Requires-Dist: uwsgi==2.0.26; extra == 'docker'
|
|
44
44
|
Provides-Extra: test
|
|
45
|
-
Requires-Dist: factory-boy==3.
|
|
45
|
+
Requires-Dist: factory-boy==3.3.1; extra == 'test'
|
|
46
46
|
Requires-Dist: playwright>=1.39; extra == 'test'
|
|
47
|
-
Requires-Dist: pytest-django==4.
|
|
48
|
-
Requires-Dist: pytest-playwright==0.5.
|
|
47
|
+
Requires-Dist: pytest-django==4.9.0; extra == 'test'
|
|
48
|
+
Requires-Dist: pytest-playwright==0.5.2; extra == 'test'
|
|
49
49
|
Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'
|
|
50
|
-
Requires-Dist: pytest==8.
|
|
50
|
+
Requires-Dist: pytest==8.3.2; extra == 'test'
|
|
51
51
|
Description-Content-Type: text/markdown
|
|
52
52
|
|
|
53
53
|
# uMap project
|