Compare commits

...

4 commits

Author SHA1 Message Date
Yohan Boniface
09faa763f9
feat: add a back button to importers dialog (#2364)
Some checks failed
Test & Docs / tests (postgresql, 3.10) (push) Has been cancelled
Test & Docs / tests (postgresql, 3.12) (push) Has been cancelled
Test & Docs / lint (push) Has been cancelled
Test & Docs / docs (push) Has been cancelled
![image](https://github.com/user-attachments/assets/99445f06-f48f-4b0b-88f6-f696e067416d)
2024-12-13 15:49:45 +01:00
Yohan Boniface
fe06a0ff8e chore: add more tests for team related views 2024-12-13 14:44:54 +01:00
Yohan Boniface
b578ceced4 feat: add a back button to importers dialog 2024-12-13 12:27:41 +01:00
Yohan Boniface
7c808393f0 chore: make all backends point to S3 in the documentation example 2024-12-13 11:20:33 +01:00
6 changed files with 144 additions and 81 deletions

View file

@ -40,24 +40,40 @@ Then, change the `STORAGES` settings with something like this:
``` ```
STORAGES = { STORAGES = {
"default": { "default": {
"BACKEND": "django.core.files.storage.FileSystemStorage", "BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
"access_key": "xxx",
"secret_key": "yyy",
"bucket_name": "umap-pictograms",
"endpoint_url": "http://127.0.0.1:9000",
},
}, },
"data": { "data": {
# Whatch out, this is a dedicated uMap class!
"BACKEND": "umap.storage.s3.S3DataStorage", "BACKEND": "umap.storage.s3.S3DataStorage",
"OPTIONS": { "OPTIONS": {
"access_key": "xxx", "access_key": "xxx",
"secret_key": "yyy", "secret_key": "yyy",
"bucket_name": "umap", "bucket_name": "umap-data",
"region_name": "eu",
"endpoint_url": "http://127.0.0.1:9000", "endpoint_url": "http://127.0.0.1:9000",
}, },
}, },
"staticfiles": { "staticfiles": {
"BACKEND": "umap.storage.staticfiles.UmapManifestStaticFilesStorage", "BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
"access_key": "xxx",
"secret_key": "yyy",
"bucket_name": "umapstatics",
"endpoint_url": "http://127.0.0.1:9000",
},
}, },
} }
``` ```
As you can see in this example, both `staticfiles` and `default` use the storage class provided
by `django-storages` (`storages.backends.s3.S3Storage`), but the `data` one uses a specific class
(`umap.storage.s3.S3DataStorage`).
In order to store old versions of a layer, the versioning should be activated in the bucket. In order to store old versions of a layer, the versioning should be activated in the bucket.
See more about the configuration on the [django-storages documentation](https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html). See more about the configuration on the [django-storages documentation](https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html).

View file

@ -64,7 +64,10 @@ export default class Importer extends Utils.WithTemplate {
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap'] this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
this.IMPORTERS = [] this.IMPORTERS = []
this.loadImporters() this.loadImporters()
this.dialog = new Dialog({ className: 'importers dark' }) this.dialog = new Dialog({
className: 'importers dark',
back: () => this.showImporters(),
})
} }
loadImporters() { loadImporters() {
@ -172,7 +175,7 @@ export default class Importer extends Utils.WithTemplate {
button.addEventListener('click', () => plugin.open(this)) button.addEventListener('click', () => plugin.open(this))
grid.appendChild(button) grid.appendChild(button)
} }
this.dialog.open({ template: element, cancel: false, accept: false }) this.dialog.open({ template: element, cancel: false, accept: false, back: false })
} }
build() { build() {

View file

@ -6,6 +6,7 @@ const TEMPLATE = `
<form method="dialog" data-ref="form"> <form method="dialog" data-ref="form">
<ul class="buttons"> <ul class="buttons">
<li><i class="icon icon-16 icon-close" data-close></i></li> <li><i class="icon icon-16 icon-close" data-close></i></li>
<li hidden data-ref="back"><i class="icon icon-16 icon-back"></i></li>
</ul> </ul>
<h3 data-ref="message" id="${Math.round(Date.now()).toString(36)}"></h3> <h3 data-ref="message" id="${Math.round(Date.now()).toString(36)}"></h3>
<fieldset data-ref="fieldset" role="document"> <fieldset data-ref="fieldset" role="document">
@ -123,6 +124,10 @@ export default class Dialog extends WithTemplate {
} else { } else {
this.elements.template.innerHTML = dialog.template || '' this.elements.template.innerHTML = dialog.template || ''
} }
this.elements.back.hidden = !dialog.back
if (dialog.back) {
this.elements.back.addEventListener('click', dialog.back)
}
this.focusable = this.getFocusable() this.focusable = this.getFocusable()
this.hasFormData = this.elements.fieldset.elements.length > 0 this.hasFormData = this.elements.fieldset.elements.length > 0

View file

@ -0,0 +1,82 @@
import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from umap.models import Map
from .base import MapFactory, UserFactory
User = get_user_model()
pytestmark = pytest.mark.django_db
def test_user_dashboard_is_restricted_to_logged_in(client):
response = client.get(reverse("user_dashboard"))
assert response.status_code == 302
assert response["Location"] == "/en/login/?next=/en/me"
def test_user_dashboard_display_user_maps(client, map):
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name in body
assert f"{map.get_absolute_url()}?edit" in body
assert f"{map.get_absolute_url()}?share" in body
assert f"/map/{map.pk}/download" in body
assert "Everyone (public)" in body
assert "Owner only" in body
def test_user_dashboard_do_not_display_blocked_user_maps(client, map):
map.share_status = Map.BLOCKED
map.save()
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name not in body
def test_user_dashboard_do_not_display_deleted_user_maps(client, map):
map.share_status = Map.DELETED
map.save()
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name not in body
@pytest.mark.parametrize("share_status", [Map.DRAFT, Map.PRIVATE, Map.PUBLIC, Map.OPEN])
def test_user_dashboard_display_user_team_maps(client, map, team, user, share_status):
user.teams.add(team)
user.save()
map.team = team
map.share_status = share_status
map.save()
assert map.owner != user
client.login(username=user.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name in body
assert map.get_absolute_url() in body
def test_user_dashboard_display_user_maps_distinct(client, map):
# cf https://github.com/umap-project/umap/issues/1325
anonymap = MapFactory(name="Map witout owner should not appear")
user1 = UserFactory(username="user1")
user2 = UserFactory(username="user2")
map.editors.add(user1)
map.editors.add(user2)
map.save()
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert body.count(f'<a href="/en/map/test-map_{map.pk}">test map</a>') == 1
assert body.count(anonymap.name) == 0

View file

@ -1,7 +1,7 @@
import pytest import pytest
from django.urls import reverse from django.urls import reverse
from umap.models import Team from umap.models import Map, Team
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -15,6 +15,37 @@ def test_can_see_team_maps(client, map, team):
assert map.name in response.content.decode() assert map.name in response.content.decode()
def test_others_cannot_see_team_private_maps_in_team_page(client, map, team, user):
map.team = team
map.share_status = Map.PRIVATE
map.save()
url = reverse("team_maps", args=(team.pk,))
response = client.get(url)
assert response.status_code == 200
assert map.name not in response.content.decode()
# User is not in team
client.login(username=user.username, password="123123")
response = client.get(url)
assert response.status_code == 200
assert map.name not in response.content.decode()
@pytest.mark.parametrize("share_status", [Map.PRIVATE, Map.DRAFT])
def test_members_can_see_private_maps_in_team_page(
client, map, team, user, share_status
):
map.team = team
map.share_status = share_status
map.save()
user.teams.add(team)
user.save()
url = reverse("team_maps", args=(team.pk,))
client.login(username=user.username, password="123123")
response = client.get(url)
assert response.status_code == 200
assert map.name not in response.content.decode()
def test_user_can_see_their_teams(client, team, user): def test_user_can_see_their_teams(client, team, user):
user.teams.add(team) user.teams.add(team)
user.save() user.save()

View file

@ -267,80 +267,6 @@ def test_change_user_slug(client, user, settings):
assert f"/en/user/{user.pk}/" in response.content.decode() assert f"/en/user/{user.pk}/" in response.content.decode()
@pytest.mark.django_db
def test_user_dashboard_is_restricted_to_logged_in(client):
response = client.get(reverse("user_dashboard"))
assert response.status_code == 302
assert response["Location"] == "/en/login/?next=/en/me"
@pytest.mark.django_db
def test_user_dashboard_display_user_maps(client, map):
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name in body
assert f"{map.get_absolute_url()}?edit" in body
assert f"{map.get_absolute_url()}?share" in body
assert f"/map/{map.pk}/download" in body
assert "Everyone (public)" in body
assert "Owner only" in body
@pytest.mark.django_db
def test_user_dashboard_do_not_display_blocked_user_maps(client, map):
map.share_status = Map.BLOCKED
map.save()
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name not in body
@pytest.mark.django_db
def test_user_dashboard_do_not_display_deleted_user_maps(client, map):
map.share_status = Map.DELETED
map.save()
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name not in body
@pytest.mark.django_db
def test_user_dashboard_display_user_team_maps(client, map, team, user):
user.teams.add(team)
user.save()
map.team = team
map.save()
client.login(username=user.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert map.name in body
assert map.get_absolute_url() in body
@pytest.mark.django_db
def test_user_dashboard_display_user_maps_distinct(client, map):
# cf https://github.com/umap-project/umap/issues/1325
anonymap = MapFactory(name="Map witout owner should not appear")
user1 = UserFactory(username="user1")
user2 = UserFactory(username="user2")
map.editors.add(user1)
map.editors.add(user2)
map.save()
client.login(username=map.owner.username, password="123123")
response = client.get(reverse("user_dashboard"))
assert response.status_code == 200
body = response.content.decode()
assert body.count(f'<a href="/en/map/test-map_{map.pk}">test map</a>') == 1
assert body.count(anonymap.name) == 0
@pytest.mark.django_db @pytest.mark.django_db
def test_logout_should_return_redirect(client, user, settings): def test_logout_should_return_redirect(client, user, settings):
client.login(username=user.username, password="123123") client.login(username=user.username, password="123123")