umap-project 1.10.0__py3-none-any.whl → 1.11.1__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 +7 -3
- umap/autocomplete.py +3 -5
- umap/bin/__init__.py +2 -5
- umap/decorators.py +4 -5
- umap/forms.py +5 -5
- umap/locale/en/LC_MESSAGES/django.po +23 -23
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +28 -27
- umap/management/commands/generate_js_locale.py +9 -11
- umap/management/commands/import_pictograms.py +49 -28
- umap/managers.py +5 -3
- umap/middleware.py +2 -3
- umap/migrations/0001_initial.py +204 -56
- umap/migrations/0002_tilelayer_tms.py +3 -4
- umap/migrations/0003_add_tilelayer.py +13 -9
- umap/migrations/0004_add_licence.py +3 -6
- umap/migrations/0005_remove_map_tilelayer.py +3 -4
- umap/migrations/0006_auto_20190407_0719.py +6 -5
- umap/migrations/0007_auto_20190416_1757.py +13 -5
- umap/migrations/0008_alter_map_settings.py +0 -1
- umap/migrations/0009_star.py +27 -8
- umap/migrations/0010_alter_map_edit_status_alter_map_share_status.py +20 -8
- umap/migrations/0011_alter_map_edit_status_alter_map_share_status.py +21 -8
- umap/migrations/0014_map_created_at.py +1 -1
- umap/migrations/0015_alter_pictogram_pictogram.py +17 -0
- umap/migrations/0016_pictogram_category.py +17 -0
- umap/models.py +9 -7
- umap/settings/base.py +1 -4
- umap/static/umap/base.css +59 -20
- umap/static/umap/content.css +2 -13
- umap/static/umap/favicons/apple-touch-icon.png +0 -0
- umap/static/umap/favicons/favicon.ico +0 -0
- umap/static/umap/favicons/icon-192.png +0 -0
- umap/static/umap/favicons/icon-512.png +0 -0
- umap/static/umap/favicons/icon.svg +5 -0
- umap/static/umap/img/16-white.svg +20 -9
- umap/static/umap/img/24-white.svg +2 -2
- umap/static/umap/img/source/16-white.svg +25 -13
- umap/static/umap/img/source/24-white.svg +5 -5
- umap/static/umap/js/umap.controls.js +15 -23
- umap/static/umap/js/umap.core.js +33 -24
- umap/static/umap/js/umap.features.js +24 -2
- umap/static/umap/js/umap.forms.js +182 -84
- umap/static/umap/js/umap.icon.js +19 -14
- umap/static/umap/js/umap.js +14 -32
- umap/static/umap/js/umap.layer.js +13 -2
- umap/static/umap/js/umap.popup.js +21 -0
- umap/static/umap/locale/am_ET.js +9 -4
- umap/static/umap/locale/am_ET.json +9 -4
- umap/static/umap/locale/ar.js +9 -4
- umap/static/umap/locale/ar.json +9 -4
- umap/static/umap/locale/ast.js +9 -4
- umap/static/umap/locale/ast.json +9 -4
- umap/static/umap/locale/bg.js +9 -4
- umap/static/umap/locale/bg.json +9 -4
- umap/static/umap/locale/br.js +20 -15
- umap/static/umap/locale/br.json +20 -15
- umap/static/umap/locale/ca.js +9 -4
- umap/static/umap/locale/ca.json +9 -4
- umap/static/umap/locale/cs_CZ.js +9 -4
- umap/static/umap/locale/cs_CZ.json +9 -4
- umap/static/umap/locale/da.js +9 -4
- umap/static/umap/locale/da.json +9 -4
- umap/static/umap/locale/de.js +9 -4
- umap/static/umap/locale/de.json +9 -4
- umap/static/umap/locale/el.js +9 -4
- umap/static/umap/locale/el.json +9 -4
- umap/static/umap/locale/en.js +9 -4
- umap/static/umap/locale/en.json +9 -4
- umap/static/umap/locale/en_US.json +9 -4
- umap/static/umap/locale/es.js +15 -10
- umap/static/umap/locale/es.json +15 -10
- umap/static/umap/locale/et.js +9 -4
- umap/static/umap/locale/et.json +9 -4
- umap/static/umap/locale/fa_IR.js +9 -4
- umap/static/umap/locale/fa_IR.json +9 -4
- umap/static/umap/locale/fi.js +9 -4
- umap/static/umap/locale/fi.json +9 -4
- umap/static/umap/locale/fr.js +10 -5
- umap/static/umap/locale/fr.json +10 -5
- umap/static/umap/locale/gl.js +9 -4
- umap/static/umap/locale/gl.json +9 -4
- umap/static/umap/locale/he.js +9 -4
- umap/static/umap/locale/he.json +9 -4
- umap/static/umap/locale/hr.js +9 -4
- umap/static/umap/locale/hr.json +9 -4
- umap/static/umap/locale/hu.js +64 -59
- umap/static/umap/locale/hu.json +64 -59
- umap/static/umap/locale/id.js +9 -4
- umap/static/umap/locale/id.json +9 -4
- umap/static/umap/locale/is.js +9 -4
- umap/static/umap/locale/is.json +9 -4
- umap/static/umap/locale/it.js +9 -4
- umap/static/umap/locale/it.json +9 -4
- umap/static/umap/locale/ja.js +9 -4
- umap/static/umap/locale/ja.json +9 -4
- umap/static/umap/locale/ko.js +9 -4
- umap/static/umap/locale/ko.json +9 -4
- umap/static/umap/locale/lt.js +9 -4
- umap/static/umap/locale/lt.json +9 -4
- umap/static/umap/locale/ms.js +15 -10
- umap/static/umap/locale/ms.json +15 -10
- umap/static/umap/locale/nl.js +9 -4
- umap/static/umap/locale/nl.json +9 -4
- umap/static/umap/locale/no.js +9 -4
- umap/static/umap/locale/no.json +9 -4
- umap/static/umap/locale/pl.js +9 -4
- umap/static/umap/locale/pl.json +9 -4
- umap/static/umap/locale/pl_PL.json +9 -4
- umap/static/umap/locale/pt.js +9 -4
- umap/static/umap/locale/pt.json +9 -4
- umap/static/umap/locale/pt_BR.js +9 -4
- umap/static/umap/locale/pt_BR.json +9 -4
- umap/static/umap/locale/pt_PT.js +9 -4
- umap/static/umap/locale/pt_PT.json +9 -4
- umap/static/umap/locale/ro.js +9 -4
- umap/static/umap/locale/ro.json +9 -4
- umap/static/umap/locale/ru.js +9 -4
- umap/static/umap/locale/ru.json +9 -4
- umap/static/umap/locale/si.js +55 -20
- umap/static/umap/locale/si.json +55 -20
- umap/static/umap/locale/sk_SK.js +9 -4
- umap/static/umap/locale/sk_SK.json +9 -4
- umap/static/umap/locale/sl.js +9 -4
- umap/static/umap/locale/sl.json +9 -4
- umap/static/umap/locale/sr.js +9 -4
- umap/static/umap/locale/sr.json +9 -4
- umap/static/umap/locale/sv.js +9 -4
- umap/static/umap/locale/sv.json +9 -4
- umap/static/umap/locale/th_TH.js +9 -4
- umap/static/umap/locale/th_TH.json +9 -4
- umap/static/umap/locale/tr.js +9 -4
- umap/static/umap/locale/tr.json +9 -4
- umap/static/umap/locale/uk_UA.js +9 -4
- umap/static/umap/locale/uk_UA.json +9 -4
- umap/static/umap/locale/vi.js +9 -4
- umap/static/umap/locale/vi.json +9 -4
- umap/static/umap/locale/vi_VN.json +9 -4
- umap/static/umap/locale/zh.js +9 -4
- umap/static/umap/locale/zh.json +9 -4
- umap/static/umap/locale/zh_CN.json +9 -4
- umap/static/umap/locale/zh_TW.Big5.json +9 -4
- umap/static/umap/locale/zh_TW.js +9 -4
- umap/static/umap/locale/zh_TW.json +9 -4
- umap/static/umap/map.css +104 -21
- umap/static/umap/nav.css +0 -1
- umap/static/umap/test/Controls.js +0 -1
- umap/static/umap/test/Feature.js +29 -0
- umap/static/umap/test/Map.Export.js +2 -127
- umap/static/umap/test/Util.js +10 -0
- umap/static/umap/test/_pre.js +2 -3
- umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +13 -2
- umap/static/umap/vendors/leaflet/leaflet-src.js +7144 -7144
- umap/templates/auth/user_form.html +2 -2
- umap/templates/base.html +11 -0
- umap/templates/umap/map_table.html +3 -3
- umap/templatetags/umap_tags.py +32 -34
- umap/tests/base.py +4 -4
- umap/tests/conftest.py +3 -6
- umap/tests/fixtures/circle.svg +4 -0
- umap/tests/fixtures/star.svg +4 -0
- umap/tests/integration/test_export_map.py +5 -18
- umap/tests/integration/test_picto.py +217 -0
- umap/tests/integration/test_slideshow.py +70 -0
- umap/tests/settings.py +11 -5
- umap/tests/test_datalayer.py +7 -6
- umap/tests/test_map.py +6 -5
- umap/tests/test_map_views.py +82 -10
- umap/tests/test_tilelayer.py +17 -11
- umap/tests/test_views.py +36 -9
- umap/urls.py +27 -4
- umap/utils.py +1 -2
- umap/views.py +67 -35
- umap/wsgi.py +2 -2
- {umap_project-1.10.0.dist-info → umap_project-1.11.1.dist-info}/METADATA +11 -9
- {umap_project-1.10.0.dist-info → umap_project-1.11.1.dist-info}/RECORD +180 -170
- umap/static/favicon.ico +0 -0
- {umap_project-1.10.0.dist-info → umap_project-1.11.1.dist-info}/WHEEL +0 -0
- {umap_project-1.10.0.dist-info → umap_project-1.11.1.dist-info}/entry_points.txt +0 -0
- {umap_project-1.10.0.dist-info → umap_project-1.11.1.dist-info}/licenses/LICENSE +0 -0
umap/tests/test_map_views.py
CHANGED
|
@@ -3,8 +3,8 @@ import json
|
|
|
3
3
|
import pytest
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
5
|
from django.core import mail
|
|
6
|
-
from django.urls import reverse
|
|
7
6
|
from django.core.signing import Signer
|
|
7
|
+
from django.urls import reverse
|
|
8
8
|
|
|
9
9
|
from umap.models import DataLayer, Map, Star
|
|
10
10
|
|
|
@@ -275,9 +275,7 @@ def test_owner_cannot_access_map_with_share_status_blocked(client, map):
|
|
|
275
275
|
assert response.status_code == 403
|
|
276
276
|
|
|
277
277
|
|
|
278
|
-
def test_non_editor_cannot_access_map_if_share_status_private(
|
|
279
|
-
client, map, user
|
|
280
|
-
): # noqa
|
|
278
|
+
def test_non_editor_cannot_access_map_if_share_status_private(client, map, user): # noqa
|
|
281
279
|
url = reverse("map", args=(map.slug, map.pk))
|
|
282
280
|
map.share_status = map.PRIVATE
|
|
283
281
|
map.save()
|
|
@@ -355,9 +353,7 @@ def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): #
|
|
|
355
353
|
|
|
356
354
|
|
|
357
355
|
@pytest.mark.usefixtures("allow_anonymous")
|
|
358
|
-
def test_anonymous_update_with_cookie_should_work(
|
|
359
|
-
cookieclient, anonymap, post_data
|
|
360
|
-
): # noqa
|
|
356
|
+
def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data): # noqa
|
|
361
357
|
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
|
362
358
|
# POST only mendatory fields
|
|
363
359
|
name = "new map name"
|
|
@@ -438,9 +434,7 @@ def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(
|
|
|
438
434
|
|
|
439
435
|
|
|
440
436
|
@pytest.mark.usefixtures("allow_anonymous")
|
|
441
|
-
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(
|
|
442
|
-
client, anonymap
|
|
443
|
-
): # noqa
|
|
437
|
+
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap): # noqa
|
|
444
438
|
assert Map.objects.count() == 1
|
|
445
439
|
url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
|
|
446
440
|
anonymap.edit_status = anonymap.ANONYMOUS
|
|
@@ -603,3 +597,81 @@ def test_can_send_link_on_anonymous_map_with_cookie(cookieclient, anonymap):
|
|
|
603
597
|
assert resp.status_code == 200
|
|
604
598
|
assert len(mail.outbox) == 1
|
|
605
599
|
assert mail.outbox[0].subject == "The uMap edit link for your map: test map"
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def test_download(client, map, datalayer):
|
|
603
|
+
url = reverse("map_download", args=(map.pk,))
|
|
604
|
+
response = client.get(url)
|
|
605
|
+
assert response.status_code == 200
|
|
606
|
+
# Test response is a json
|
|
607
|
+
j = json.loads(response.content.decode())
|
|
608
|
+
assert j["type"] == "umap"
|
|
609
|
+
assert j["uri"] == f"http://testserver/en/map/test-map_{map.pk}"
|
|
610
|
+
assert j["geometry"] == {
|
|
611
|
+
"coordinates": [13.447265624999998, 48.94415123418794],
|
|
612
|
+
"type": "Point",
|
|
613
|
+
}
|
|
614
|
+
assert j["properties"] == {
|
|
615
|
+
"datalayersControl": True,
|
|
616
|
+
"description": "Which is just the Danube, at the end",
|
|
617
|
+
"displayPopupFooter": False,
|
|
618
|
+
"licence": "",
|
|
619
|
+
"miniMap": False,
|
|
620
|
+
"moreControl": True,
|
|
621
|
+
"name": "test map",
|
|
622
|
+
"scaleControl": True,
|
|
623
|
+
"tilelayer": {
|
|
624
|
+
"attribution": "© OSM Contributors",
|
|
625
|
+
"maxZoom": 18,
|
|
626
|
+
"minZoom": 0,
|
|
627
|
+
"url_template": "http://{s}.osm.fr/{z}/{x}/{y}.png",
|
|
628
|
+
},
|
|
629
|
+
"tilelayersControl": True,
|
|
630
|
+
"zoom": 7,
|
|
631
|
+
"zoomControl": True,
|
|
632
|
+
}
|
|
633
|
+
assert j["layers"] == [
|
|
634
|
+
{
|
|
635
|
+
"_umap_options": {
|
|
636
|
+
"browsable": True,
|
|
637
|
+
"displayOnLoad": True,
|
|
638
|
+
"name": "test datalayer",
|
|
639
|
+
},
|
|
640
|
+
"features": [
|
|
641
|
+
{
|
|
642
|
+
"geometry": {
|
|
643
|
+
"coordinates": [13.68896484375, 48.55297816440071],
|
|
644
|
+
"type": "Point",
|
|
645
|
+
},
|
|
646
|
+
"properties": {
|
|
647
|
+
"_umap_options": {"color": "DarkCyan", "iconClass": "Ball"},
|
|
648
|
+
"description": "Da place anonymous again 755",
|
|
649
|
+
"name": "Here",
|
|
650
|
+
},
|
|
651
|
+
"type": "Feature",
|
|
652
|
+
}
|
|
653
|
+
],
|
|
654
|
+
"type": "FeatureCollection",
|
|
655
|
+
},
|
|
656
|
+
]
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.BLOCKED])
|
|
660
|
+
def test_download_shared_status_map(client, map, datalayer, share_status):
|
|
661
|
+
map.share_status = share_status
|
|
662
|
+
map.save()
|
|
663
|
+
url = reverse("map_download", args=(map.pk,))
|
|
664
|
+
response = client.get(url)
|
|
665
|
+
assert response.status_code == 403
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def test_download_my_map(client, map, datalayer):
|
|
669
|
+
map.share_status = Map.PRIVATE
|
|
670
|
+
map.save()
|
|
671
|
+
client.login(username=map.owner.username, password="123123")
|
|
672
|
+
url = reverse("map_download", args=(map.pk,))
|
|
673
|
+
response = client.get(url)
|
|
674
|
+
assert response.status_code == 200
|
|
675
|
+
# Test response is a json
|
|
676
|
+
j = json.loads(response.content.decode())
|
|
677
|
+
assert j["type"] == "umap"
|
umap/tests/test_tilelayer.py
CHANGED
|
@@ -6,16 +6,22 @@ pytestmark = pytest.mark.django_db
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def test_tilelayer_json():
|
|
9
|
-
tilelayer = TileLayerFactory(
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
tilelayer = TileLayerFactory(
|
|
10
|
+
attribution="Attribution",
|
|
11
|
+
maxZoom=19,
|
|
12
|
+
minZoom=0,
|
|
13
|
+
name="Name",
|
|
14
|
+
rank=1,
|
|
15
|
+
tms=True,
|
|
16
|
+
url_template="http://{s}.x.fr/{z}/{x}/{y}",
|
|
17
|
+
)
|
|
12
18
|
assert tilelayer.json == {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
"attribution": "Attribution",
|
|
20
|
+
"id": tilelayer.id,
|
|
21
|
+
"maxZoom": 19,
|
|
22
|
+
"minZoom": 0,
|
|
23
|
+
"name": "Name",
|
|
24
|
+
"rank": 1,
|
|
25
|
+
"tms": True,
|
|
26
|
+
"url_template": "http://{s}.x.fr/{z}/{x}/{y}",
|
|
21
27
|
}
|
umap/tests/test_views.py
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import socket
|
|
3
|
-
from datetime import date, timedelta
|
|
3
|
+
from datetime import date, datetime, timedelta
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
from django.conf import settings
|
|
7
7
|
from django.contrib.auth import get_user, get_user_model
|
|
8
|
-
from django.urls import reverse
|
|
9
8
|
from django.test import RequestFactory
|
|
9
|
+
from django.urls import reverse
|
|
10
|
+
from django.utils.timezone import make_aware
|
|
10
11
|
|
|
11
12
|
from umap import VERSION
|
|
12
13
|
from umap.views import validate_url
|
|
13
14
|
|
|
14
|
-
from .base import
|
|
15
|
+
from .base import MapFactory, UserFactory
|
|
15
16
|
|
|
16
17
|
User = get_user_model()
|
|
17
18
|
|
|
@@ -186,9 +187,9 @@ def test_stats_empty(client):
|
|
|
186
187
|
|
|
187
188
|
@pytest.mark.django_db
|
|
188
189
|
def test_stats_basic(client, map, datalayer, user2):
|
|
189
|
-
map.owner.last_login =
|
|
190
|
+
map.owner.last_login = make_aware(datetime.now())
|
|
190
191
|
map.owner.save()
|
|
191
|
-
user2.last_login =
|
|
192
|
+
user2.last_login = make_aware(datetime.now()) - timedelta(days=8)
|
|
192
193
|
user2.save()
|
|
193
194
|
response = client.get(reverse("stats"))
|
|
194
195
|
assert json.loads(response.content.decode()) == {
|
|
@@ -280,7 +281,7 @@ def test_user_dashboard_display_user_maps(client, map):
|
|
|
280
281
|
assert map.name in body
|
|
281
282
|
assert f"{map.get_absolute_url()}?edit" in body
|
|
282
283
|
assert f"{map.get_absolute_url()}?share" in body
|
|
283
|
-
assert f"{map.
|
|
284
|
+
assert f"/map/{map.pk}/download" in body
|
|
284
285
|
assert "Everyone (public)" in body
|
|
285
286
|
assert "Owner only" in body
|
|
286
287
|
|
|
@@ -289,8 +290,8 @@ def test_user_dashboard_display_user_maps(client, map):
|
|
|
289
290
|
def test_user_dashboard_display_user_maps_distinct(client, map):
|
|
290
291
|
# cf https://github.com/umap-project/umap/issues/1325
|
|
291
292
|
anonymap = MapFactory(name="Map witout owner should not appear")
|
|
292
|
-
user1 = UserFactory(username=
|
|
293
|
-
user2 = UserFactory(username=
|
|
293
|
+
user1 = UserFactory(username="user1")
|
|
294
|
+
user2 = UserFactory(username="user2")
|
|
294
295
|
map.editors.add(user1)
|
|
295
296
|
map.editors.add(user2)
|
|
296
297
|
map.save()
|
|
@@ -298,7 +299,7 @@ def test_user_dashboard_display_user_maps_distinct(client, map):
|
|
|
298
299
|
response = client.get(reverse("user_dashboard"))
|
|
299
300
|
assert response.status_code == 200
|
|
300
301
|
body = response.content.decode()
|
|
301
|
-
assert body.count(map.
|
|
302
|
+
assert body.count(f'<a href="/en/map/test-map_{map.pk}">test map</a>') == 1
|
|
302
303
|
assert body.count(anonymap.name) == 0
|
|
303
304
|
|
|
304
305
|
|
|
@@ -364,3 +365,29 @@ def test_user_profile_does_not_allow_to_edit_other_fields(client, map):
|
|
|
364
365
|
user = User.objects.get(pk=map.owner.pk)
|
|
365
366
|
assert user.email != new_email
|
|
366
367
|
assert user.is_superuser is False
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_favicon_redirection(client):
|
|
371
|
+
response = client.get("/favicon.ico")
|
|
372
|
+
assert response.status_code == 302
|
|
373
|
+
assert response.url == "/static/umap/favicons/favicon.ico"
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def test_webmanifest(client):
|
|
377
|
+
response = client.get("/manifest.webmanifest")
|
|
378
|
+
assert response.status_code == 200
|
|
379
|
+
assert response["content-type"] == "application/json"
|
|
380
|
+
assert response.json() == {
|
|
381
|
+
"icons": [
|
|
382
|
+
{
|
|
383
|
+
"sizes": "192x192",
|
|
384
|
+
"src": "/static/umap/favicons/icon-192.png",
|
|
385
|
+
"type": "image/png",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
"sizes": "512x512",
|
|
389
|
+
"src": "/static/umap/favicons/icon-512.png",
|
|
390
|
+
"type": "image/png",
|
|
391
|
+
},
|
|
392
|
+
]
|
|
393
|
+
}
|
umap/urls.py
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
|
-
from django.urls import include, path, re_path
|
|
3
2
|
from django.conf.urls.i18n import i18n_patterns
|
|
4
3
|
from django.conf.urls.static import static
|
|
5
4
|
from django.contrib import admin
|
|
6
5
|
from django.contrib.auth import views as auth_views
|
|
7
6
|
from django.contrib.auth.decorators import login_required
|
|
7
|
+
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
8
8
|
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
|
9
|
+
from django.urls import include, path, re_path
|
|
9
10
|
from django.views.decorators.cache import cache_control, cache_page, never_cache
|
|
10
11
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
|
12
|
+
from django.views.generic.base import RedirectView
|
|
11
13
|
|
|
12
14
|
from . import views
|
|
13
15
|
from .decorators import (
|
|
14
|
-
jsonize_view,
|
|
15
|
-
login_required_if_not_anonymous_allowed,
|
|
16
16
|
can_edit_map,
|
|
17
17
|
can_view_map,
|
|
18
|
+
jsonize_view,
|
|
19
|
+
login_required_if_not_anonymous_allowed,
|
|
18
20
|
)
|
|
19
21
|
from .utils import decorated_patterns
|
|
20
22
|
|
|
@@ -39,6 +41,11 @@ urlpatterns = [
|
|
|
39
41
|
),
|
|
40
42
|
re_path(r"^i18n/", include("django.conf.urls.i18n")),
|
|
41
43
|
re_path(r"^agnocomplete/", include("agnocomplete.urls")),
|
|
44
|
+
re_path(
|
|
45
|
+
r"^map/(?P<map_id>\d+)/download/",
|
|
46
|
+
can_view_map(views.MapDownload.as_view()),
|
|
47
|
+
name="map_download",
|
|
48
|
+
),
|
|
42
49
|
]
|
|
43
50
|
|
|
44
51
|
i18n_urls = [
|
|
@@ -183,7 +190,23 @@ urlpatterns += i18n_patterns(
|
|
|
183
190
|
re_path(r"^user/(?P<identifier>.+)/$", views.user_maps, name="user_maps"),
|
|
184
191
|
re_path(r"", include(i18n_urls)),
|
|
185
192
|
)
|
|
186
|
-
urlpatterns += (
|
|
193
|
+
urlpatterns += (
|
|
194
|
+
path("stats/", cache_page(60 * 60)(views.stats), name="stats"),
|
|
195
|
+
path(
|
|
196
|
+
"favicon.ico",
|
|
197
|
+
cache_control(max_age=60 * 60 * 24, immutable=True, public=True)(
|
|
198
|
+
RedirectView.as_view(
|
|
199
|
+
url=staticfiles_storage.url("umap/favicons/favicon.ico")
|
|
200
|
+
)
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
path(
|
|
204
|
+
"manifest.webmanifest",
|
|
205
|
+
cache_control(max_age=60 * 60 * 24, immutable=True, public=True)(
|
|
206
|
+
views.webmanifest
|
|
207
|
+
),
|
|
208
|
+
),
|
|
209
|
+
)
|
|
187
210
|
|
|
188
211
|
if settings.DEBUG and settings.MEDIA_ROOT:
|
|
189
212
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
umap/utils.py
CHANGED
umap/views.py
CHANGED
|
@@ -3,18 +3,20 @@ import mimetypes
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
import socket
|
|
6
|
-
from datetime import
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
7
|
from http.client import InvalidURL
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from urllib.error import URLError
|
|
10
|
-
from urllib.parse import quote
|
|
9
|
+
from urllib.error import HTTPError, URLError
|
|
10
|
+
from urllib.parse import quote, urlparse
|
|
11
|
+
from urllib.request import Request, build_opener
|
|
11
12
|
|
|
12
13
|
from django.conf import settings
|
|
13
14
|
from django.contrib import messages
|
|
14
|
-
from django.contrib.auth import logout as do_logout
|
|
15
15
|
from django.contrib.auth import get_user_model
|
|
16
|
+
from django.contrib.auth import logout as do_logout
|
|
16
17
|
from django.contrib.gis.measure import D
|
|
17
18
|
from django.contrib.postgres.search import SearchQuery, SearchVector
|
|
19
|
+
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
18
20
|
from django.core.mail import send_mail
|
|
19
21
|
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
|
20
22
|
from django.core.signing import BadSignature, Signer
|
|
@@ -32,8 +34,11 @@ from django.shortcuts import get_object_or_404
|
|
|
32
34
|
from django.urls import reverse, reverse_lazy
|
|
33
35
|
from django.utils.encoding import smart_bytes
|
|
34
36
|
from django.utils.http import http_date
|
|
37
|
+
from django.utils.timezone import make_aware
|
|
35
38
|
from django.utils.translation import gettext as _
|
|
36
39
|
from django.utils.translation import to_locale
|
|
40
|
+
from django.views.decorators.cache import cache_control
|
|
41
|
+
from django.views.decorators.http import require_GET
|
|
37
42
|
from django.views.generic import DetailView, TemplateView, View
|
|
38
43
|
from django.views.generic.base import RedirectView
|
|
39
44
|
from django.views.generic.detail import BaseDetailView
|
|
@@ -42,13 +47,13 @@ from django.views.generic.list import ListView
|
|
|
42
47
|
|
|
43
48
|
from . import VERSION
|
|
44
49
|
from .forms import (
|
|
50
|
+
DEFAULT_CENTER,
|
|
45
51
|
DEFAULT_LATITUDE,
|
|
46
52
|
DEFAULT_LONGITUDE,
|
|
47
|
-
DEFAULT_CENTER,
|
|
48
|
-
DataLayerForm,
|
|
49
|
-
DataLayerPermissionsForm,
|
|
50
53
|
AnonymousDataLayerPermissionsForm,
|
|
51
54
|
AnonymousMapPermissionsForm,
|
|
55
|
+
DataLayerForm,
|
|
56
|
+
DataLayerPermissionsForm,
|
|
52
57
|
FlatErrorList,
|
|
53
58
|
MapSettingsForm,
|
|
54
59
|
SendLinkForm,
|
|
@@ -58,16 +63,6 @@ from .forms import (
|
|
|
58
63
|
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
|
59
64
|
from .utils import get_uri_template, gzip_file, is_ajax
|
|
60
65
|
|
|
61
|
-
try:
|
|
62
|
-
# python3
|
|
63
|
-
from urllib.parse import urlparse
|
|
64
|
-
from urllib.request import Request, build_opener
|
|
65
|
-
from urllib.error import HTTPError
|
|
66
|
-
except ImportError:
|
|
67
|
-
from urlparse import urlparse
|
|
68
|
-
from urllib2 import Request, HTTPError, build_opener
|
|
69
|
-
|
|
70
|
-
|
|
71
66
|
User = get_user_model()
|
|
72
67
|
|
|
73
68
|
|
|
@@ -137,8 +132,6 @@ class Home(PaginatorMixin, TemplateView, PublicMapsMixin):
|
|
|
137
132
|
demo_map = Map.public.get(pk=settings.UMAP_DEMO_PK)
|
|
138
133
|
except Map.DoesNotExist:
|
|
139
134
|
pass
|
|
140
|
-
else:
|
|
141
|
-
maps = maps.exclude(id=demo_map.pk)
|
|
142
135
|
|
|
143
136
|
showcase_map = None
|
|
144
137
|
if hasattr(settings, "UMAP_SHOWCASE_PK"):
|
|
@@ -146,8 +139,6 @@ class Home(PaginatorMixin, TemplateView, PublicMapsMixin):
|
|
|
146
139
|
showcase_map = Map.public.get(pk=settings.UMAP_SHOWCASE_PK)
|
|
147
140
|
except Map.DoesNotExist:
|
|
148
141
|
pass
|
|
149
|
-
else:
|
|
150
|
-
maps = maps.exclude(id=showcase_map.pk)
|
|
151
142
|
|
|
152
143
|
maps = self.paginate(maps, settings.UMAP_MAPS_PER_PAGE)
|
|
153
144
|
|
|
@@ -202,13 +193,10 @@ class UserMaps(PaginatorMixin, DetailView):
|
|
|
202
193
|
return settings.UMAP_MAPS_PER_PAGE_OWNER
|
|
203
194
|
return settings.UMAP_MAPS_PER_PAGE
|
|
204
195
|
|
|
205
|
-
def get_map_queryset(self):
|
|
206
|
-
return Map.objects if self.is_owner() else Map.public
|
|
207
|
-
|
|
208
196
|
def get_maps(self):
|
|
209
|
-
qs =
|
|
210
|
-
qs = qs.filter(
|
|
211
|
-
return qs.
|
|
197
|
+
qs = Map.public
|
|
198
|
+
qs = qs.filter(owner=self.object).union(qs.filter(editors=self.object))
|
|
199
|
+
return qs.order_by("-modified_at")
|
|
212
200
|
|
|
213
201
|
def get_context_data(self, **kwargs):
|
|
214
202
|
kwargs.update({"maps": self.paginate(self.get_maps(), self.per_page)})
|
|
@@ -222,9 +210,8 @@ class UserStars(UserMaps):
|
|
|
222
210
|
template_name = "auth/user_stars.html"
|
|
223
211
|
|
|
224
212
|
def get_maps(self):
|
|
225
|
-
qs = self.get_map_queryset()
|
|
226
213
|
stars = Star.objects.filter(by=self.object).values("map")
|
|
227
|
-
qs =
|
|
214
|
+
qs = Map.public.filter(pk__in=stars)
|
|
228
215
|
return qs.order_by("-modified_at")
|
|
229
216
|
|
|
230
217
|
|
|
@@ -408,7 +395,7 @@ def _urls_for_js(urls=None):
|
|
|
408
395
|
"""
|
|
409
396
|
if urls is None:
|
|
410
397
|
# prevent circular import
|
|
411
|
-
from .urls import
|
|
398
|
+
from .urls import i18n_urls, urlpatterns
|
|
412
399
|
|
|
413
400
|
urls = [
|
|
414
401
|
url.name for url in urlpatterns + i18n_urls if getattr(url, "name", None)
|
|
@@ -419,7 +406,7 @@ def _urls_for_js(urls=None):
|
|
|
419
406
|
|
|
420
407
|
|
|
421
408
|
def simple_json_response(**kwargs):
|
|
422
|
-
return HttpResponse(json.dumps(kwargs))
|
|
409
|
+
return HttpResponse(json.dumps(kwargs), content_type="application/json")
|
|
423
410
|
|
|
424
411
|
|
|
425
412
|
# ############## #
|
|
@@ -469,9 +456,7 @@ class MapDetailMixin:
|
|
|
469
456
|
else:
|
|
470
457
|
map_statuses = AnonymousMapPermissionsForm.STATUS
|
|
471
458
|
datalayer_statuses = AnonymousDataLayerPermissionsForm.STATUS
|
|
472
|
-
properties["edit_statuses"] = [
|
|
473
|
-
(i, str(label)) for i, label in map_statuses
|
|
474
|
-
]
|
|
459
|
+
properties["edit_statuses"] = [(i, str(label)) for i, label in map_statuses]
|
|
475
460
|
properties["datalayer_edit_statuses"] = [
|
|
476
461
|
(i, str(label)) for i, label in datalayer_statuses
|
|
477
462
|
]
|
|
@@ -606,6 +591,32 @@ class MapView(MapDetailMixin, PermissionsMixin, DetailView):
|
|
|
606
591
|
return Star.objects.filter(by=user, map=self.object).exists()
|
|
607
592
|
|
|
608
593
|
|
|
594
|
+
class MapDownload(DetailView):
|
|
595
|
+
model = Map
|
|
596
|
+
pk_url_kwarg = "map_id"
|
|
597
|
+
|
|
598
|
+
def get_canonical_url(self):
|
|
599
|
+
return reverse("map_download", args=(self.object.pk,))
|
|
600
|
+
|
|
601
|
+
def render_to_response(self, context, *args, **kwargs):
|
|
602
|
+
geojson = self.object.settings
|
|
603
|
+
geojson["type"] = "umap"
|
|
604
|
+
geojson["uri"] = self.request.build_absolute_uri(self.object.get_absolute_url())
|
|
605
|
+
datalayers = []
|
|
606
|
+
for datalayer in self.object.datalayer_set.all():
|
|
607
|
+
with open(datalayer.geojson.path, "rb") as f:
|
|
608
|
+
layer = json.loads(f.read())
|
|
609
|
+
if datalayer.settings:
|
|
610
|
+
layer["_umap_options"] = datalayer.settings
|
|
611
|
+
datalayers.append(layer)
|
|
612
|
+
geojson["layers"] = datalayers
|
|
613
|
+
response = simple_json_response(**geojson)
|
|
614
|
+
response[
|
|
615
|
+
"Content-Disposition"
|
|
616
|
+
] = f'attachment; filename="umap_backup_{self.object.slug}.umap"'
|
|
617
|
+
return response
|
|
618
|
+
|
|
619
|
+
|
|
609
620
|
class MapViewGeoJSON(MapView):
|
|
610
621
|
def get_canonical_url(self):
|
|
611
622
|
return reverse("map_geojson", args=(self.object.pk,))
|
|
@@ -1000,7 +1011,7 @@ class PictogramJSONList(ListView):
|
|
|
1000
1011
|
|
|
1001
1012
|
|
|
1002
1013
|
def stats(request):
|
|
1003
|
-
last_week =
|
|
1014
|
+
last_week = make_aware(datetime.now()) - timedelta(days=7)
|
|
1004
1015
|
return simple_json_response(
|
|
1005
1016
|
**{
|
|
1006
1017
|
"version": VERSION,
|
|
@@ -1016,6 +1027,27 @@ def stats(request):
|
|
|
1016
1027
|
)
|
|
1017
1028
|
|
|
1018
1029
|
|
|
1030
|
+
@require_GET
|
|
1031
|
+
@cache_control(max_age=60 * 60 * 24, immutable=True, public=True) # One day.
|
|
1032
|
+
def webmanifest(request):
|
|
1033
|
+
return simple_json_response(
|
|
1034
|
+
**{
|
|
1035
|
+
"icons": [
|
|
1036
|
+
{
|
|
1037
|
+
"src": staticfiles_storage.url("umap/favicons/icon-192.png"),
|
|
1038
|
+
"type": "image/png",
|
|
1039
|
+
"sizes": "192x192",
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
"src": staticfiles_storage.url("umap/favicons/icon-512.png"),
|
|
1043
|
+
"type": "image/png",
|
|
1044
|
+
"sizes": "512x512",
|
|
1045
|
+
},
|
|
1046
|
+
]
|
|
1047
|
+
}
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
|
|
1019
1051
|
def logout(request):
|
|
1020
1052
|
do_logout(request)
|
|
1021
1053
|
if is_ajax(request):
|
umap/wsgi.py
CHANGED
|
@@ -15,13 +15,13 @@ framework.
|
|
|
15
15
|
"""
|
|
16
16
|
import os
|
|
17
17
|
|
|
18
|
-
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
|
19
|
-
"umap.settings")
|
|
18
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "umap.settings")
|
|
20
19
|
|
|
21
20
|
# This application object is used by any WSGI server configured to use this
|
|
22
21
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
|
23
22
|
# setting points here.
|
|
24
23
|
from django.core.wsgi import get_wsgi_application
|
|
24
|
+
|
|
25
25
|
application = get_wsgi_application()
|
|
26
26
|
|
|
27
27
|
# Apply WSGI middleware here.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: umap-project
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.1
|
|
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>
|
|
@@ -11,37 +11,39 @@ Classifier: Intended Audience :: Developers
|
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.4
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.5
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
18
14
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
20
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
20
|
Requires-Python: >=3.8
|
|
23
21
|
Requires-Dist: django-agnocomplete==2.2.0
|
|
24
22
|
Requires-Dist: django-compressor==4.3.1
|
|
25
23
|
Requires-Dist: django-environ==0.10.0
|
|
26
24
|
Requires-Dist: django-probes==1.7.0
|
|
27
|
-
Requires-Dist: django
|
|
25
|
+
Requires-Dist: django<5,>=4.2
|
|
28
26
|
Requires-Dist: pillow==10.0.1
|
|
29
27
|
Requires-Dist: psycopg2==2.9.6
|
|
30
28
|
Requires-Dist: requests==2.31.0
|
|
31
29
|
Requires-Dist: social-auth-app-django==5.2.0
|
|
32
30
|
Requires-Dist: social-auth-core==4.4.2
|
|
33
31
|
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: black==23.3.0; extra == 'dev'
|
|
35
32
|
Requires-Dist: djlint==1.31.0; extra == 'dev'
|
|
36
33
|
Requires-Dist: hatch==1.7.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: isort==5.12; extra == 'dev'
|
|
37
35
|
Requires-Dist: mkdocs==1.5.2; extra == 'dev'
|
|
36
|
+
Requires-Dist: pymdown-extensions==10.4; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff==0.1.6; extra == 'dev'
|
|
38
|
+
Requires-Dist: vermin==1.5.2; extra == 'dev'
|
|
38
39
|
Provides-Extra: docker
|
|
39
40
|
Requires-Dist: uwsgi==2.0.21; extra == 'docker'
|
|
40
41
|
Provides-Extra: test
|
|
41
42
|
Requires-Dist: factory-boy==3.2.1; extra == 'test'
|
|
42
|
-
Requires-Dist: playwright
|
|
43
|
+
Requires-Dist: playwright<2,>=1.39; extra == 'test'
|
|
43
44
|
Requires-Dist: pytest-django==4.5.2; extra == 'test'
|
|
44
|
-
Requires-Dist: pytest-playwright
|
|
45
|
+
Requires-Dist: pytest-playwright<1,>=0.4.3; extra == 'test'
|
|
46
|
+
Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'
|
|
45
47
|
Requires-Dist: pytest==6.2.5; extra == 'test'
|
|
46
48
|
Description-Content-Type: text/markdown
|
|
47
49
|
|