mirror of
https://github.com/umap-project/umap.git
synced 2025-05-06 06:21:49 +02:00
parent
87814f0362
commit
6e9e46dbbd
14 changed files with 174 additions and 154 deletions
|
@ -33,6 +33,7 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Django>=4.1",
|
"Django>=4.1",
|
||||||
|
"djangorestframework==3.14.0",
|
||||||
"django-agnocomplete==2.2.0",
|
"django-agnocomplete==2.2.0",
|
||||||
"django-compressor==4.3.1",
|
"django-compressor==4.3.1",
|
||||||
"django-environ==0.10.0",
|
"django-environ==0.10.0",
|
||||||
|
|
59
umap/api_views.py
Normal file
59
umap/api_views.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
from rest_framework import generics, status
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from .models import Map
|
||||||
|
from .serializers import MapSerializer
|
||||||
|
from .views import ANONYMOUS_COOKIE_MAX_AGE
|
||||||
|
|
||||||
|
|
||||||
|
class MapList(generics.ListCreateAPIView):
|
||||||
|
queryset = Map.public.all().order_by("-modified_at")
|
||||||
|
serializer_class = MapSerializer
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
if self.request.user.is_authenticated:
|
||||||
|
serializer.save(owner=self.request.user)
|
||||||
|
else:
|
||||||
|
serializer.save()
|
||||||
|
|
||||||
|
def get_map_permissions(self, map_):
|
||||||
|
permissions = {}
|
||||||
|
permissions["edit_status"] = map_.edit_status
|
||||||
|
permissions["share_status"] = map_.share_status
|
||||||
|
if map_.owner:
|
||||||
|
permissions["owner"] = {
|
||||||
|
"id": map_.owner.pk,
|
||||||
|
"name": str(map_.owner),
|
||||||
|
"url": map_.owner.get_url(),
|
||||||
|
}
|
||||||
|
permissions["editors"] = [
|
||||||
|
{"id": editor.pk, "name": str(editor)} for editor in map_.editors.all()
|
||||||
|
]
|
||||||
|
if not map_.owner and map_.is_anonymous_owner(self.request):
|
||||||
|
permissions["anonymous_edit_url"] = map_.get_anonymous_edit_url()
|
||||||
|
return permissions
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_create(serializer)
|
||||||
|
map_ = serializer.instance
|
||||||
|
headers = self.get_success_headers(serializer.data)
|
||||||
|
data = serializer.data
|
||||||
|
permissions = self.get_map_permissions(map_)
|
||||||
|
if not map_.owner:
|
||||||
|
anonymous_url = map_.get_anonymous_edit_url()
|
||||||
|
permissions["anonymous_edit_url"] = anonymous_url
|
||||||
|
data["permissions"] = permissions
|
||||||
|
response = Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
if not self.request.user.is_authenticated:
|
||||||
|
key, value = map_.signed_cookie_elements
|
||||||
|
response.set_signed_cookie(
|
||||||
|
key=key, value=value, max_age=ANONYMOUS_COOKIE_MAX_AGE
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class MapDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
queryset = Map.public.all().order_by("-modified_at")
|
||||||
|
serializer_class = MapSerializer
|
|
@ -33,7 +33,7 @@ def can_edit_map(view_func):
|
||||||
|
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
map_inst = get_object_or_404(Map, pk=kwargs["map_id"])
|
map_inst = get_object_or_404(Map, pk=kwargs.get("map_id", kwargs.get("pk")))
|
||||||
user = request.user
|
user = request.user
|
||||||
kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view
|
kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view
|
||||||
if map_inst.edit_status >= map_inst.EDITORS:
|
if map_inst.edit_status >= map_inst.EDITORS:
|
||||||
|
@ -54,7 +54,7 @@ def can_view_map(view_func):
|
||||||
|
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
map_inst = get_object_or_404(Map, pk=kwargs["map_id"])
|
map_inst = get_object_or_404(Map, pk=kwargs.get("map_id", kwargs.get("pk")))
|
||||||
kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view
|
kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view
|
||||||
if not map_inst.can_view(request):
|
if not map_inst.can_view(request):
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
|
@ -2,7 +2,6 @@ from django import forms
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.template.defaultfilters import slugify
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.forms.utils import ErrorList
|
from django.forms.utils import ErrorList
|
||||||
|
|
||||||
|
@ -78,34 +77,6 @@ class AnonymousDataLayerPermissionsForm(forms.ModelForm):
|
||||||
fields = ("edit_status",)
|
fields = ("edit_status",)
|
||||||
|
|
||||||
|
|
||||||
class MapSettingsForm(forms.ModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(MapSettingsForm, self).__init__(*args, **kwargs)
|
|
||||||
self.fields["slug"].required = False
|
|
||||||
self.fields["center"].widget.map_srid = 4326
|
|
||||||
|
|
||||||
def clean_slug(self):
|
|
||||||
slug = self.cleaned_data.get("slug", None)
|
|
||||||
name = self.cleaned_data.get("name", None)
|
|
||||||
if not slug and name:
|
|
||||||
# If name is empty, don't do nothing, validation will raise
|
|
||||||
# later on the process because name is required
|
|
||||||
self.cleaned_data["slug"] = slugify(name) or "map"
|
|
||||||
return self.cleaned_data["slug"][:50]
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def clean_center(self):
|
|
||||||
if not self.cleaned_data["center"]:
|
|
||||||
point = DEFAULT_CENTER
|
|
||||||
self.cleaned_data["center"] = point
|
|
||||||
return self.cleaned_data["center"]
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
fields = ("settings", "name", "center", "slug")
|
|
||||||
model = Map
|
|
||||||
|
|
||||||
|
|
||||||
class UserProfileForm(forms.ModelForm):
|
class UserProfileForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
|
32
umap/serializers.py
Normal file
32
umap/serializers.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
|
from django.conf import settings
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Map
|
||||||
|
|
||||||
|
DEFAULT_LATITUDE = (
|
||||||
|
settings.LEAFLET_LATITUDE if hasattr(settings, "LEAFLET_LATITUDE") else 51
|
||||||
|
)
|
||||||
|
DEFAULT_LONGITUDE = (
|
||||||
|
settings.LEAFLET_LONGITUDE if hasattr(settings, "LEAFLET_LONGITUDE") else 2
|
||||||
|
)
|
||||||
|
DEFAULT_CENTER = Point(DEFAULT_LONGITUDE, DEFAULT_LATITUDE)
|
||||||
|
|
||||||
|
|
||||||
|
class MapSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
slug = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Map
|
||||||
|
fields = ["id", "name", "slug", "center", "settings"]
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
slug = data.get("slug")
|
||||||
|
name = data.get("name")
|
||||||
|
if not slug and name:
|
||||||
|
data["slug"] = slugify(name)[:50] or "map"
|
||||||
|
return data
|
||||||
|
|
||||||
|
def validate_center(self, value):
|
||||||
|
return value or DEFAULT_CENTER
|
|
@ -122,6 +122,7 @@ INSTALLED_APPS = (
|
||||||
"umap",
|
"umap",
|
||||||
"compressor",
|
"compressor",
|
||||||
"social_django",
|
"social_django",
|
||||||
|
"rest_framework",
|
||||||
# See https://github.com/peopledoc/django-agnocomplete/commit/26eda2dfa4a2f8a805ca2ea19a0c504b9d773a1c
|
# See https://github.com/peopledoc/django-agnocomplete/commit/26eda2dfa4a2f8a805ca2ea19a0c504b9d773a1c
|
||||||
# Django does not find the app config in the default place, so the app is not loaded
|
# Django does not find the app config in the default place, so the app is not loaded
|
||||||
# so the "autodiscover" is not run.
|
# so the "autodiscover" is not run.
|
||||||
|
@ -281,6 +282,11 @@ if SOCIAL_AUTH_OPENSTREETMAP_KEY and SOCIAL_AUTH_OPENSTREETMAP_SECRET:
|
||||||
)
|
)
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS += ("django.contrib.auth.backends.ModelBackend",)
|
AUTHENTICATION_BACKENDS += ("django.contrib.auth.backends.ModelBackend",)
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
# Use Django's standard `django.contrib.auth` permissions,
|
||||||
|
# or allow read-only access for unauthenticated users.
|
||||||
|
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.AllowAny"]
|
||||||
|
}
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
|
|
@ -1102,7 +1102,8 @@ L.U.Map.include({
|
||||||
formData.append('name', this.options.name)
|
formData.append('name', this.options.name)
|
||||||
formData.append('center', JSON.stringify(this.geometry()))
|
formData.append('center', JSON.stringify(this.geometry()))
|
||||||
formData.append('settings', JSON.stringify(geojson))
|
formData.append('settings', JSON.stringify(geojson))
|
||||||
this.post(this.getSaveUrl(), {
|
const method = this.options.umap_id ? this.put.bind(this) : this.post.bind(this)
|
||||||
|
method(this.getSaveUrl(), {
|
||||||
data: formData,
|
data: formData,
|
||||||
context: this,
|
context: this,
|
||||||
callback: function (data) {
|
callback: function (data) {
|
||||||
|
@ -1192,13 +1193,13 @@ L.U.Map.include({
|
||||||
},
|
},
|
||||||
|
|
||||||
getEditUrl: function () {
|
getEditUrl: function () {
|
||||||
return L.Util.template(this.options.urls.map_update, {
|
return L.Util.template(this.options.urls.map_detail, {
|
||||||
map_id: this.options.umap_id,
|
pk: this.options.umap_id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getCreateUrl: function () {
|
getCreateUrl: function () {
|
||||||
return L.Util.template(this.options.urls.map_create)
|
return L.Util.template(this.options.urls.map_list)
|
||||||
},
|
},
|
||||||
|
|
||||||
getSaveUrl: function () {
|
getSaveUrl: function () {
|
||||||
|
@ -1855,6 +1856,12 @@ L.U.Map.include({
|
||||||
this.xhr.post(url, options)
|
this.xhr.post(url, options)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
put: function (url, options) {
|
||||||
|
options = options || {}
|
||||||
|
options.listener = this
|
||||||
|
this.xhr.put(url, options)
|
||||||
|
},
|
||||||
|
|
||||||
get: function (url, options) {
|
get: function (url, options) {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
options.listener = this
|
options.listener = this
|
||||||
|
|
|
@ -60,7 +60,7 @@ L.U.Xhr = L.Evented.extend({
|
||||||
|
|
||||||
xhr.onreadystatechange = () => {
|
xhr.onreadystatechange = () => {
|
||||||
if (xhr.readyState === 4) {
|
if (xhr.readyState === 4) {
|
||||||
if (xhr.status == 200) {
|
if (xhr.status == 200 || xhr.status == 201) {
|
||||||
settings.callback.call(settings.context || xhr, xhr.responseText, xhr)
|
settings.callback.call(settings.context || xhr, xhr.responseText, xhr)
|
||||||
} else if (xhr.status === 403) {
|
} else if (xhr.status === 403) {
|
||||||
self.ui.alert({
|
self.ui.alert({
|
||||||
|
@ -130,7 +130,7 @@ L.U.Xhr = L.Evented.extend({
|
||||||
}
|
}
|
||||||
const settings = L.Util.extend({}, default_options, options)
|
const settings = L.Util.extend({}, default_options, options)
|
||||||
|
|
||||||
if (verb === 'POST') {
|
if (verb === 'POST' || verb === 'PUT') {
|
||||||
// find a way not to make this django specific
|
// find a way not to make this django specific
|
||||||
const token = document.cookie.replace(
|
const token = document.cookie.replace(
|
||||||
/(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/,
|
/(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/,
|
||||||
|
@ -186,6 +186,10 @@ L.U.Xhr = L.Evented.extend({
|
||||||
this._json('POST', uri, options)
|
this._json('POST', uri, options)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
put: function (uri, options) {
|
||||||
|
this._json('PUT', uri, options)
|
||||||
|
},
|
||||||
|
|
||||||
submit_form: function (form_id, options) {
|
submit_form: function (form_id, options) {
|
||||||
if (typeof options === 'undefined') options = {}
|
if (typeof options === 'undefined') options = {}
|
||||||
const form = L.DomUtil.get(form_id)
|
const form = L.DomUtil.get(form_id)
|
||||||
|
|
|
@ -76,11 +76,7 @@ describe('L.U.DataLayer', function () {
|
||||||
it('should call datalayer.save on save button click', function (done) {
|
it('should call datalayer.save on save button click', function (done) {
|
||||||
sinon.spy(this.datalayer, 'save')
|
sinon.spy(this.datalayer, 'save')
|
||||||
this.server.flush()
|
this.server.flush()
|
||||||
this.server.respondWith(
|
this.server.respondWith('PUT', '/map/99/', JSON.stringify({ id: 99 }))
|
||||||
'POST',
|
|
||||||
'/map/99/update/settings/',
|
|
||||||
JSON.stringify({ id: 99 })
|
|
||||||
)
|
|
||||||
this.server.respondWith(
|
this.server.respondWith(
|
||||||
'POST',
|
'POST',
|
||||||
'/map/99/datalayer/update/62/',
|
'/map/99/datalayer/update/62/',
|
||||||
|
@ -97,11 +93,7 @@ describe('L.U.DataLayer', function () {
|
||||||
it('should show alert if server respond 412', function () {
|
it('should show alert if server respond 412', function () {
|
||||||
cleanAlert()
|
cleanAlert()
|
||||||
this.server.flush()
|
this.server.flush()
|
||||||
this.server.respondWith(
|
this.server.respondWith('PUT', '/map/99/', JSON.stringify({ id: 99 }))
|
||||||
'POST',
|
|
||||||
'/map/99/update/settings/',
|
|
||||||
JSON.stringify({ id: 99 })
|
|
||||||
)
|
|
||||||
this.server.respondWith('POST', '/map/99/datalayer/update/62/', [412, {}, ''])
|
this.server.respondWith('POST', '/map/99/datalayer/update/62/', [412, {}, ''])
|
||||||
happen.click(editButton)
|
happen.click(editButton)
|
||||||
input = qs('form.umap-form input[name="name"]')
|
input = qs('form.umap-form input[name="name"]')
|
||||||
|
@ -178,11 +170,7 @@ describe('L.U.DataLayer', function () {
|
||||||
it('should set umap_id on save callback', function () {
|
it('should set umap_id on save callback', function () {
|
||||||
assert.notOk(newDatalayer.umap_id)
|
assert.notOk(newDatalayer.umap_id)
|
||||||
this.server.flush()
|
this.server.flush()
|
||||||
this.server.respondWith(
|
this.server.respondWith('PUT', '/map/99/', JSON.stringify({ id: 99 }))
|
||||||
'POST',
|
|
||||||
'/map/99/update/settings/',
|
|
||||||
JSON.stringify({ id: 99 })
|
|
||||||
)
|
|
||||||
this.server.respondWith(
|
this.server.respondWith(
|
||||||
'POST',
|
'POST',
|
||||||
'/map/99/datalayer/create/',
|
'/map/99/datalayer/create/',
|
||||||
|
@ -219,11 +207,7 @@ describe('L.U.DataLayer', function () {
|
||||||
}
|
}
|
||||||
var spy = sinon.spy(response)
|
var spy = sinon.spy(response)
|
||||||
this.server.flush()
|
this.server.flush()
|
||||||
this.server.respondWith(
|
this.server.respondWith('PUT', '/map/99/', JSON.stringify({ id: 99 }))
|
||||||
'POST',
|
|
||||||
'/map/99/update/settings/',
|
|
||||||
JSON.stringify({ id: 99 })
|
|
||||||
)
|
|
||||||
this.server.respondWith('POST', '/map/99/datalayer/update/63/', spy)
|
this.server.respondWith('POST', '/map/99/datalayer/update/63/', spy)
|
||||||
clickSave()
|
clickSave()
|
||||||
this.server.respond()
|
this.server.respond()
|
||||||
|
@ -404,8 +388,7 @@ describe('L.U.DataLayer', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("#displayOnLoad", function () {
|
describe('#displayOnLoad', function () {
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.server.respondWith(
|
this.server.respondWith(
|
||||||
/\/datalayer\/64\/\?.*/,
|
/\/datalayer\/64\/\?.*/,
|
||||||
|
@ -416,26 +399,25 @@ describe('L.U.DataLayer', function () {
|
||||||
this.datalayer.fetchData()
|
this.datalayer.fetchData()
|
||||||
this.server.respond()
|
this.server.respond()
|
||||||
this.map.setZoom(10, { animate: false })
|
this.map.setZoom(10, { animate: false })
|
||||||
});
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
this.datalayer._delete()
|
this.datalayer._delete()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not display layer at load", function () {
|
it('should not display layer at load', function () {
|
||||||
assert.notOk(qs('path[fill="AliceBlue"]'))
|
assert.notOk(qs('path[fill="AliceBlue"]'))
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should display on click", function () {
|
it('should display on click', function () {
|
||||||
happen.click(qs(`[data-id='${L.stamp(this.datalayer)}'] .layer-toggle`))
|
happen.click(qs(`[data-id='${L.stamp(this.datalayer)}'] .layer-toggle`))
|
||||||
assert.ok(qs('path[fill="AliceBlue"]'))
|
assert.ok(qs('path[fill="AliceBlue"]'))
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not display on zoom", function () {
|
it('should not display on zoom', function () {
|
||||||
this.map.setZoom(9, { animate: false })
|
this.map.setZoom(9, { animate: false })
|
||||||
assert.notOk(qs('path[fill="AliceBlue"]'))
|
assert.notOk(qs('path[fill="AliceBlue"]'))
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#facet-search()', function () {
|
describe('#facet-search()', function () {
|
||||||
|
|
|
@ -114,7 +114,8 @@ function initMap(options) {
|
||||||
urls: {
|
urls: {
|
||||||
map: '/map/{slug}_{pk}',
|
map: '/map/{slug}_{pk}',
|
||||||
datalayer_view: '/datalayer/{pk}/',
|
datalayer_view: '/datalayer/{pk}/',
|
||||||
map_update: '/map/{map_id}/update/settings/',
|
map_list: '/api/maps/',
|
||||||
|
map_detail: '/api/maps/{map_id}/',
|
||||||
map_old_url: '/map/{username}/{slug}/',
|
map_old_url: '/map/{username}/{slug}/',
|
||||||
map_clone: '/map/{map_id}/update/clone/',
|
map_clone: '/map/{map_id}/update/clone/',
|
||||||
map_short_url: '/m/{pk}/',
|
map_short_url: '/m/{pk}/',
|
||||||
|
@ -122,7 +123,6 @@ function initMap(options) {
|
||||||
map_new: '/map/new/',
|
map_new: '/map/new/',
|
||||||
datalayer_update: '/map/{map_id}/datalayer/update/{pk}/',
|
datalayer_update: '/map/{map_id}/datalayer/update/{pk}/',
|
||||||
map_delete: '/map/{map_id}/update/delete/',
|
map_delete: '/map/{map_id}/update/delete/',
|
||||||
map_create: '/map/create/',
|
|
||||||
logout: '/logout/',
|
logout: '/logout/',
|
||||||
datalayer_create: '/map/{map_id}/datalayer/create/',
|
datalayer_create: '/map/{map_id}/datalayer/create/',
|
||||||
login_popup_end: '/login/popupd/',
|
login_popup_end: '/login/popupd/',
|
||||||
|
|
|
@ -50,11 +50,13 @@ def test_editors_can_edit_if_status_editors(map, user):
|
||||||
assert map.can_edit(user)
|
assert map.can_edit(user)
|
||||||
|
|
||||||
|
|
||||||
def test_logged_in_user_should_be_allowed_for_anonymous_map_with_anonymous_edit_status(map, user, rf): # noqa
|
def test_logged_in_user_should_be_allowed_for_anonymous_map_with_anonymous_edit_status(
|
||||||
|
map, user, rf
|
||||||
|
): # noqa
|
||||||
map.owner = None
|
map.owner = None
|
||||||
map.edit_status = map.ANONYMOUS
|
map.edit_status = map.ANONYMOUS
|
||||||
map.save()
|
map.save()
|
||||||
url = reverse('map_update', kwargs={'map_id': map.pk})
|
url = reverse("map_detail", kwargs={"pk": map.pk})
|
||||||
request = rf.get(url)
|
request = rf.get(url)
|
||||||
request.user = user
|
request.user = user
|
||||||
assert map.can_edit(user, request)
|
assert map.can_edit(user, request)
|
||||||
|
@ -70,7 +72,7 @@ def test_anonymous_user_should_not_be_allowed_for_anonymous_map(map, user, rf):
|
||||||
def test_clone_should_return_new_instance(map, user):
|
def test_clone_should_return_new_instance(map, user):
|
||||||
clone = map.clone()
|
clone = map.clone()
|
||||||
assert map.pk != clone.pk
|
assert map.pk != clone.pk
|
||||||
assert u"Clone of " + map.name == clone.name
|
assert "Clone of " + map.name == clone.name
|
||||||
assert map.settings == clone.settings
|
assert map.settings == clone.settings
|
||||||
assert map.center == clone.center
|
assert map.center == clone.center
|
||||||
assert map.zoom == clone.zoom
|
assert map.zoom == clone.zoom
|
||||||
|
@ -108,8 +110,7 @@ def test_clone_should_clone_datalayers_and_features_too(map, user, datalayer):
|
||||||
def test_publicmanager_should_get_only_public_maps(map, user, licence):
|
def test_publicmanager_should_get_only_public_maps(map, user, licence):
|
||||||
map.share_status = map.PUBLIC
|
map.share_status = map.PUBLIC
|
||||||
open_map = MapFactory(owner=user, licence=licence, share_status=Map.OPEN)
|
open_map = MapFactory(owner=user, licence=licence, share_status=Map.OPEN)
|
||||||
private_map = MapFactory(owner=user, licence=licence,
|
private_map = MapFactory(owner=user, licence=licence, share_status=Map.PRIVATE)
|
||||||
share_status=Map.PRIVATE)
|
|
||||||
assert map in Map.public.all()
|
assert map in Map.public.all()
|
||||||
assert open_map not in Map.public.all()
|
assert open_map not in Map.public.all()
|
||||||
assert private_map not in Map.public.all()
|
assert private_map not in Map.public.all()
|
||||||
|
|
|
@ -24,13 +24,13 @@ def post_data():
|
||||||
|
|
||||||
|
|
||||||
def test_create(client, user, post_data):
|
def test_create(client, user, post_data):
|
||||||
url = reverse("map_create")
|
url = reverse("map_list")
|
||||||
# POST only mendatory fields
|
# POST only mandatory fields
|
||||||
name = "test-map-with-new-name"
|
name = "test-map-with-new-name"
|
||||||
post_data["name"] = name
|
post_data["name"] = name
|
||||||
client.login(username=user.username, password="123123")
|
client.login(username=user.username, password="123123")
|
||||||
response = client.post(url, post_data)
|
response = client.post(url, post_data)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 201
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
created_map = Map.objects.latest("pk")
|
created_map = Map.objects.latest("pk")
|
||||||
assert j["id"] == created_map.pk
|
assert j["id"] == created_map.pk
|
||||||
|
@ -45,21 +45,22 @@ def test_create(client, user, post_data):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_map_create_permissions(client, settings):
|
def test_map_list_permissions(client, settings):
|
||||||
settings.UMAP_ALLOW_ANONYMOUS = False
|
settings.UMAP_ALLOW_ANONYMOUS = False
|
||||||
url = reverse("map_create")
|
url = reverse("map_list")
|
||||||
# POST anonymous
|
# POST anonymous
|
||||||
response = client.post(url, {})
|
response = client.post(url, {})
|
||||||
|
assert response.status_code == 200
|
||||||
assert login_required(response)
|
assert login_required(response)
|
||||||
|
|
||||||
|
|
||||||
def test_map_update_access(client, map, user):
|
def test_map_update_access(client, map, user):
|
||||||
url = reverse("map_update", kwargs={"map_id": map.pk})
|
url = reverse("map_detail", kwargs={"pk": map.pk})
|
||||||
# GET anonymous
|
# GET anonymous
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
assert login_required(response)
|
assert login_required(response)
|
||||||
# POST anonymous
|
# POST anonymous
|
||||||
response = client.post(url, {})
|
response = client.put(url, {}, content_type="application/json")
|
||||||
assert login_required(response)
|
assert login_required(response)
|
||||||
# GET with wrong permissions
|
# GET with wrong permissions
|
||||||
client.login(username=user.username, password="123123")
|
client.login(username=user.username, password="123123")
|
||||||
|
@ -67,7 +68,7 @@ def test_map_update_access(client, map, user):
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
# POST with wrong permissions
|
# POST with wrong permissions
|
||||||
client.login(username=user.username, password="123123")
|
client.login(username=user.username, password="123123")
|
||||||
response = client.post(url, {})
|
response = client.put(url, {}, content_type="application/json")
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,12 +91,12 @@ def test_map_update_permissions_access(client, map, user):
|
||||||
|
|
||||||
|
|
||||||
def test_update(client, map, post_data):
|
def test_update(client, map, post_data):
|
||||||
url = reverse("map_update", kwargs={"map_id": map.pk})
|
url = reverse("map_detail", kwargs={"pk": map.pk})
|
||||||
# POST only mendatory fields
|
# PUT only mandatory fields
|
||||||
name = "new map name"
|
name = "new map name"
|
||||||
post_data["name"] = name
|
post_data["name"] = name
|
||||||
client.login(username=map.owner.username, password="123123")
|
client.login(username=map.owner.username, password="123123")
|
||||||
response = client.post(url, post_data)
|
response = client.put(url, post_data, content_type="application/json")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
assert "html" not in j
|
assert "html" not in j
|
||||||
|
@ -198,13 +199,13 @@ def test_clone_should_set_cloner_as_owner(client, map, user):
|
||||||
|
|
||||||
|
|
||||||
def test_map_creation_should_allow_unicode_names(client, map, post_data):
|
def test_map_creation_should_allow_unicode_names(client, map, post_data):
|
||||||
url = reverse("map_create")
|
url = reverse("map_list")
|
||||||
# POST only mendatory fields
|
# POST only mandatory fields
|
||||||
name = "Академический"
|
name = "Академический"
|
||||||
post_data["name"] = name
|
post_data["name"] = name
|
||||||
client.login(username=map.owner.username, password="123123")
|
client.login(username=map.owner.username, password="123123")
|
||||||
response = client.post(url, post_data)
|
response = client.post(url, post_data)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 201
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
created_map = Map.objects.latest("pk")
|
created_map = Map.objects.latest("pk")
|
||||||
assert j["id"] == created_map.pk
|
assert j["id"] == created_map.pk
|
||||||
|
@ -317,25 +318,25 @@ def test_logged_in_user_can_edit_map_editable_by_anonymous(client, map, user):
|
||||||
map.edit_status = map.ANONYMOUS
|
map.edit_status = map.ANONYMOUS
|
||||||
map.save()
|
map.save()
|
||||||
client.login(username=user.username, password="123123")
|
client.login(username=user.username, password="123123")
|
||||||
url = reverse("map_update", kwargs={"map_id": map.pk})
|
url = reverse("map_detail", kwargs={"pk": map.pk})
|
||||||
new_name = "this is my new name"
|
new_name = "this is my new name"
|
||||||
data = {
|
data = {
|
||||||
"center": '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
|
"center": '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
|
||||||
"name": new_name,
|
"name": new_name,
|
||||||
}
|
}
|
||||||
response = client.post(url, data)
|
response = client.put(url, data, content_type="application/json")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert Map.objects.get(pk=map.pk).name == new_name
|
assert Map.objects.get(pk=map.pk).name == new_name
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("allow_anonymous")
|
@pytest.mark.usefixtures("allow_anonymous")
|
||||||
def test_anonymous_create(cookieclient, post_data):
|
def test_anonymous_create(cookieclient, post_data):
|
||||||
url = reverse("map_create")
|
url = reverse("map_list")
|
||||||
# POST only mendatory fields
|
# POST only mandatory fields
|
||||||
name = "test-map-with-new-name"
|
name = "test-map-with-new-name"
|
||||||
post_data["name"] = name
|
post_data["name"] = name
|
||||||
response = cookieclient.post(url, post_data)
|
response = cookieclient.post(url, post_data)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 201
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
created_map = Map.objects.latest("pk")
|
created_map = Map.objects.latest("pk")
|
||||||
assert j["id"] == created_map.pk
|
assert j["id"] == created_map.pk
|
||||||
|
@ -349,8 +350,8 @@ def test_anonymous_create(cookieclient, post_data):
|
||||||
|
|
||||||
@pytest.mark.usefixtures("allow_anonymous")
|
@pytest.mark.usefixtures("allow_anonymous")
|
||||||
def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): # noqa
|
def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): # noqa
|
||||||
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
url = reverse("map_detail", kwargs={"pk": anonymap.pk})
|
||||||
response = client.post(url, post_data)
|
response = client.put(url, post_data, content_type="application/json")
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
@ -358,11 +359,11 @@ def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): #
|
||||||
def test_anonymous_update_with_cookie_should_work(
|
def test_anonymous_update_with_cookie_should_work(
|
||||||
cookieclient, anonymap, post_data
|
cookieclient, anonymap, post_data
|
||||||
): # noqa
|
): # noqa
|
||||||
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
url = reverse("map_detail", kwargs={"pk": anonymap.pk})
|
||||||
# POST only mendatory fields
|
# POST only mandatory fields
|
||||||
name = "new map name"
|
name = "new map name"
|
||||||
post_data["name"] = name
|
post_data["name"] = name
|
||||||
response = cookieclient.post(url, post_data)
|
response = cookieclient.put(url, post_data, content_type="application/json")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
j = json.loads(response.content.decode())
|
j = json.loads(response.content.decode())
|
||||||
updated_map = Map.objects.get(pk=anonymap.pk)
|
updated_map = Map.objects.get(pk=anonymap.pk)
|
||||||
|
@ -522,7 +523,7 @@ def test_map_attach_owner_anonymous_not_allowed(cookieclient, anonymap, user):
|
||||||
|
|
||||||
def test_create_readonly(client, user, post_data, settings):
|
def test_create_readonly(client, user, post_data, settings):
|
||||||
settings.UMAP_READONLY = True
|
settings.UMAP_READONLY = True
|
||||||
url = reverse("map_create")
|
url = reverse("map_list")
|
||||||
client.login(username=user.username, password="123123")
|
client.login(username=user.username, password="123123")
|
||||||
response = client.post(url, post_data)
|
response = client.post(url, post_data)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||||
from django.views.decorators.cache import cache_control, cache_page, never_cache
|
from django.views.decorators.cache import cache_control, cache_page, never_cache
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
|
|
||||||
|
from . import api_views
|
||||||
from . import views
|
from . import views
|
||||||
from .decorators import (
|
from .decorators import (
|
||||||
jsonize_view,
|
jsonize_view,
|
||||||
|
@ -93,7 +94,7 @@ i18n_urls += decorated_patterns(
|
||||||
)
|
)
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
[login_required_if_not_anonymous_allowed, never_cache],
|
[login_required_if_not_anonymous_allowed, never_cache],
|
||||||
re_path(r"^map/create/$", views.MapCreate.as_view(), name="map_create"),
|
path("api/maps/", api_views.MapList.as_view(), name="map_list"),
|
||||||
)
|
)
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
[login_required],
|
[login_required],
|
||||||
|
@ -115,9 +116,9 @@ i18n_urls += decorated_patterns(
|
||||||
)
|
)
|
||||||
map_urls = [
|
map_urls = [
|
||||||
re_path(
|
re_path(
|
||||||
r"^map/(?P<map_id>[\d]+)/update/settings/$",
|
r"^api/map/(?P<pk>[\d]+)/$",
|
||||||
views.MapUpdate.as_view(),
|
api_views.MapDetail.as_view(),
|
||||||
name="map_update",
|
name="map_detail",
|
||||||
),
|
),
|
||||||
re_path(
|
re_path(
|
||||||
r"^map/(?P<map_id>[\d]+)/update/permissions/$",
|
r"^map/(?P<map_id>[\d]+)/update/permissions/$",
|
||||||
|
|
|
@ -50,7 +50,6 @@ from .forms import (
|
||||||
AnonymousDataLayerPermissionsForm,
|
AnonymousDataLayerPermissionsForm,
|
||||||
AnonymousMapPermissionsForm,
|
AnonymousMapPermissionsForm,
|
||||||
FlatErrorList,
|
FlatErrorList,
|
||||||
MapSettingsForm,
|
|
||||||
SendLinkForm,
|
SendLinkForm,
|
||||||
UpdateMapPermissionsForm,
|
UpdateMapPermissionsForm,
|
||||||
UserProfileForm,
|
UserProfileForm,
|
||||||
|
@ -469,9 +468,7 @@ class MapDetailMixin:
|
||||||
else:
|
else:
|
||||||
map_statuses = AnonymousMapPermissionsForm.STATUS
|
map_statuses = AnonymousMapPermissionsForm.STATUS
|
||||||
datalayer_statuses = AnonymousDataLayerPermissionsForm.STATUS
|
datalayer_statuses = AnonymousDataLayerPermissionsForm.STATUS
|
||||||
properties["edit_statuses"] = [
|
properties["edit_statuses"] = [(i, str(label)) for i, label in map_statuses]
|
||||||
(i, str(label)) for i, label in map_statuses
|
|
||||||
]
|
|
||||||
properties["datalayer_edit_statuses"] = [
|
properties["datalayer_edit_statuses"] = [
|
||||||
(i, str(label)) for i, label in datalayer_statuses
|
(i, str(label)) for i, label in datalayer_statuses
|
||||||
]
|
]
|
||||||
|
@ -618,48 +615,6 @@ class MapNew(MapDetailMixin, TemplateView):
|
||||||
template_name = "umap/map_detail.html"
|
template_name = "umap/map_detail.html"
|
||||||
|
|
||||||
|
|
||||||
class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView):
|
|
||||||
model = Map
|
|
||||||
form_class = MapSettingsForm
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
if self.request.user.is_authenticated:
|
|
||||||
form.instance.owner = self.request.user
|
|
||||||
self.object = form.save()
|
|
||||||
permissions = self.get_permissions()
|
|
||||||
# User does not have the cookie yet.
|
|
||||||
if not self.object.owner:
|
|
||||||
anonymous_url = self.object.get_anonymous_edit_url()
|
|
||||||
permissions["anonymous_edit_url"] = anonymous_url
|
|
||||||
response = simple_json_response(
|
|
||||||
id=self.object.pk,
|
|
||||||
url=self.object.get_absolute_url(),
|
|
||||||
permissions=permissions,
|
|
||||||
)
|
|
||||||
if not self.request.user.is_authenticated:
|
|
||||||
key, value = self.object.signed_cookie_elements
|
|
||||||
response.set_signed_cookie(
|
|
||||||
key=key, value=value, max_age=ANONYMOUS_COOKIE_MAX_AGE
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class MapUpdate(FormLessEditMixin, PermissionsMixin, UpdateView):
|
|
||||||
model = Map
|
|
||||||
form_class = MapSettingsForm
|
|
||||||
pk_url_kwarg = "map_id"
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object.settings = form.cleaned_data["settings"]
|
|
||||||
self.object.save()
|
|
||||||
return simple_json_response(
|
|
||||||
id=self.object.pk,
|
|
||||||
url=self.object.get_absolute_url(),
|
|
||||||
permissions=self.get_permissions(),
|
|
||||||
info=_("Map has been updated!"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateMapPermissions(FormLessEditMixin, UpdateView):
|
class UpdateMapPermissions(FormLessEditMixin, UpdateView):
|
||||||
model = Map
|
model = Map
|
||||||
pk_url_kwarg = "map_id"
|
pk_url_kwarg = "map_id"
|
||||||
|
|
Loading…
Reference in a new issue