Merge pull request #1102 from umap-project/mail-link
Add a button to send edit link by email in anonymous mode
This commit is contained in:
commit
6e0c4723a4
12 changed files with 345 additions and 179 deletions
|
@ -94,3 +94,24 @@ may want to add an index. For that, you should do so:
|
|||
CREATE INDEX IF NOT EXISTS search_idx ON umap_map USING GIN(to_tsvector('umapdict', name), share_status);
|
||||
|
||||
And change `UMAP_SEARCH_CONFIGURATION = "umapdict"` in your settings.
|
||||
|
||||
|
||||
## Emails
|
||||
|
||||
UMap can send the anonymous edit link by email. For this to work, you need to
|
||||
add email specific settings. See [Django](https://docs.djangoproject.com/en/4.2/topics/email/#smtp-backend)
|
||||
documentation.
|
||||
|
||||
In general, you'll need to add something like this in your local settings:
|
||||
|
||||
```
|
||||
FROM_EMAIL = "youradmin@email.org"
|
||||
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||
EMAIL_HOST = "smtp.provider.org"
|
||||
EMAIL_PORT = 456
|
||||
EMAIL_HOST_USER = "username"
|
||||
EMAIL_HOST_PASSWORD = "xxxx"
|
||||
EMAIL_USE_TLS = True
|
||||
# or
|
||||
EMAIL_USE_SSL = True
|
||||
```
|
||||
|
|
|
@ -25,6 +25,10 @@ class FlatErrorList(ErrorList):
|
|||
return u' — '.join([e for e in self])
|
||||
|
||||
|
||||
class SendLinkForm(forms.Form):
|
||||
email = forms.EmailField()
|
||||
|
||||
|
||||
class UpdateMapPermissionsForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
|
@ -36,8 +40,7 @@ class AnonymousMapPermissionsForm(forms.ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AnonymousMapPermissionsForm, self).__init__(*args, **kwargs)
|
||||
full_secret_link = "%s%s" % (settings.SITE_URL, self.instance.get_anonymous_edit_url())
|
||||
help_text = _('Secret edit link is %s') % full_secret_link
|
||||
help_text = _('Secret edit link is %s') % self.instance.get_anonymous_edit_url()
|
||||
self.fields['edit_status'].help_text = _(help_text)
|
||||
|
||||
STATUS = (
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import sys
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.conf import settings
|
||||
|
||||
from umap.models import Map
|
||||
|
||||
|
@ -25,4 +24,4 @@ class Command(BaseCommand):
|
|||
self.abort('Map with pk {} not found'.format(pk))
|
||||
if map_.owner:
|
||||
self.abort('Map is not anonymous (owner: {})'.format(map_.owner))
|
||||
print(settings.SITE_URL + map_.get_anonymous_edit_url())
|
||||
print(map_.get_anonymous_edit_url())
|
||||
|
|
|
@ -162,7 +162,8 @@ class Map(NamedModel):
|
|||
def get_anonymous_edit_url(self):
|
||||
signer = Signer()
|
||||
signature = signer.sign(self.pk)
|
||||
return reverse("map_anonymous_edit_url", kwargs={"signature": signature})
|
||||
path = reverse("map_anonymous_edit_url", kwargs={"signature": signature})
|
||||
return settings.SITE_URL + path
|
||||
|
||||
def is_anonymous_owner(self, request):
|
||||
if self.owner:
|
||||
|
|
|
@ -114,6 +114,9 @@ INSTALLED_APPS = (
|
|||
)
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
FROM_EMAIL = None
|
||||
|
||||
# =============================================================================
|
||||
# Calculation of directories relative to the project module location
|
||||
# =============================================================================
|
||||
|
|
|
@ -734,7 +734,7 @@ input[type=hidden].blur + .button {
|
|||
.umap-alert .umap-action {
|
||||
margin-left: 10px;
|
||||
background-color: #fff;
|
||||
color: #999;
|
||||
color: #000;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
@ -748,6 +748,10 @@ input[type=hidden].blur + .button {
|
|||
.umap-alert .error .umap-action:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.umap-alert input {
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* *********** */
|
||||
/* Tooltip */
|
||||
|
|
|
@ -1332,15 +1332,75 @@ L.U.Map.include({
|
|||
formData.append('name', this.options.name)
|
||||
formData.append('center', JSON.stringify(this.geometry()))
|
||||
formData.append('settings', JSON.stringify(geojson))
|
||||
|
||||
function copyToClipboard(textToCopy) {
|
||||
// https://stackoverflow.com/a/65996386
|
||||
// Navigator clipboard api needs a secure context (https)
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(textToCopy)
|
||||
} else {
|
||||
// Use the 'out of viewport hidden text area' trick
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = textToCopy
|
||||
|
||||
// Move textarea out of the viewport so it's not visible
|
||||
textArea.style.position = 'absolute'
|
||||
textArea.style.left = '-999999px'
|
||||
|
||||
document.body.prepend(textArea)
|
||||
textArea.select()
|
||||
|
||||
try {
|
||||
document.execCommand('copy')
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
textArea.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.post(this.getSaveUrl(), {
|
||||
data: formData,
|
||||
context: this,
|
||||
callback: function (data) {
|
||||
let duration = 3000
|
||||
let duration = 3000,
|
||||
alert = { content: L._('Map has been saved!'), level: 'info' }
|
||||
if (!this.options.umap_id) {
|
||||
duration = 100000 // we want a longer message at map creation (TODO UGLY)
|
||||
alert.content = L._('Congratulations, your map has been created!')
|
||||
this.options.umap_id = data.id
|
||||
this.permissions.setOptions(data.permissions)
|
||||
if (
|
||||
data.permissions &&
|
||||
data.permissions.anonymous_edit_url &&
|
||||
this.options.urls.map_send_edit_link
|
||||
) {
|
||||
alert.duration = Infinity
|
||||
alert.content =
|
||||
L._(
|
||||
'Your map has been created! As you are not logged in, here is your secret link to edit the map, please keep it safe:'
|
||||
) + `<br>${data.permissions.anonymous_edit_url}`
|
||||
|
||||
alert.actions = [
|
||||
{
|
||||
label: L._('Send me the link'),
|
||||
input: L._('Email'),
|
||||
callback: this.sendEditLink,
|
||||
callbackContext: this,
|
||||
},
|
||||
{
|
||||
label: L._('Copy link'),
|
||||
callback: () => {
|
||||
copyToClipboard(data.permissions.anonymous_edit_url)
|
||||
this.ui.alert({
|
||||
content: L._('Secret edit link copied to clipboard!'),
|
||||
level: 'info',
|
||||
})
|
||||
},
|
||||
callbackContext: this,
|
||||
},
|
||||
]
|
||||
}
|
||||
} else if (!this.permissions.isDirty) {
|
||||
// Do not override local changes to permissions,
|
||||
// but update in case some other editors changed them in the meantime.
|
||||
|
@ -1350,11 +1410,10 @@ L.U.Map.include({
|
|||
if (history && history.pushState)
|
||||
history.pushState({}, this.options.name, data.url)
|
||||
else window.location = data.url
|
||||
if (data.info) msg = data.info
|
||||
else msg = L._('Map has been saved!')
|
||||
alert.content = data.info || alert.content
|
||||
this.once('saved', function () {
|
||||
this.isDirty = false
|
||||
this.ui.alert({ content: msg, level: 'info', duration: duration })
|
||||
this.ui.alert(alert)
|
||||
})
|
||||
this.ui.closePanel()
|
||||
this.permissions.save()
|
||||
|
@ -1362,6 +1421,20 @@ L.U.Map.include({
|
|||
})
|
||||
},
|
||||
|
||||
sendEditLink: function () {
|
||||
const url = L.Util.template(this.options.urls.map_send_edit_link, {
|
||||
map_id: this.options.umap_id,
|
||||
}),
|
||||
input = this.ui._alert.querySelector('input'),
|
||||
email = input.value
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('email', email)
|
||||
this.post(url, {
|
||||
data: formData,
|
||||
})
|
||||
},
|
||||
|
||||
getEditUrl: function () {
|
||||
return L.Util.template(this.options.urls.map_update, {
|
||||
map_id: this.options.umap_id,
|
||||
|
|
|
@ -75,7 +75,6 @@ L.U.UI = L.Evented.extend({
|
|||
},
|
||||
|
||||
popAlert: function (e) {
|
||||
const self = this
|
||||
if (!e) {
|
||||
if (this.ALERTS.length) e = this.ALERTS.pop()
|
||||
else return
|
||||
|
@ -85,8 +84,8 @@ L.U.UI = L.Evented.extend({
|
|||
this._alert.innerHTML = ''
|
||||
L.DomUtil.addClass(this.parent, 'umap-alert')
|
||||
L.DomUtil.addClass(this._alert, level_class)
|
||||
function close() {
|
||||
if (timeoutID !== this.ALERT_ID) {
|
||||
const close = () => {
|
||||
if (timeoutID && timeoutID !== this.ALERT_ID) {
|
||||
return
|
||||
} // Another alert has been forced
|
||||
this._alert.innerHTML = ''
|
||||
|
@ -108,26 +107,32 @@ L.U.UI = L.Evented.extend({
|
|||
)
|
||||
L.DomUtil.add('div', '', this._alert, e.content)
|
||||
if (e.actions) {
|
||||
let action, el
|
||||
let action, el, input
|
||||
for (let i = 0; i < e.actions.length; i++) {
|
||||
action = e.actions[i]
|
||||
if (action.input) {
|
||||
input = L.DomUtil.element(
|
||||
'input',
|
||||
{ className: 'umap-alert-input', placeholder: action.input },
|
||||
this._alert
|
||||
)
|
||||
}
|
||||
el = L.DomUtil.element('a', { className: 'umap-action' }, this._alert)
|
||||
el.href = '#'
|
||||
el.textContent = action.label
|
||||
L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', close, this)
|
||||
if (action.callback)
|
||||
L.DomEvent.on(
|
||||
el,
|
||||
'click',
|
||||
action.callback,
|
||||
action.callbackContext || this.map
|
||||
)
|
||||
L.DomEvent.on(el, 'click', L.DomEvent.stop)
|
||||
if (action.callback) {
|
||||
L.DomEvent.on(el, 'click', action.callback, action.callbackContext || this.map)
|
||||
}
|
||||
L.DomEvent.on(el, 'click', close, this)
|
||||
}
|
||||
}
|
||||
self.ALERT_ID = timeoutID = window.setTimeout(
|
||||
if (e.duration !== Infinity) {
|
||||
this.ALERT_ID = timeoutID = window.setTimeout(
|
||||
L.bind(close, this),
|
||||
e.duration || 3000
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: function (e) {
|
||||
|
|
|
@ -4,6 +4,8 @@ from umap.settings.base import * # pylint: disable=W0614,W0401
|
|||
|
||||
SECRET_KEY = "justfortests"
|
||||
COMPRESS_ENABLED = False
|
||||
FROM_EMAIL = "test@test.org"
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
|
||||
if "TRAVIS" in os.environ:
|
||||
DATABASES = {
|
||||
|
|
|
@ -2,9 +2,10 @@ import json
|
|||
|
||||
import pytest
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core import mail
|
||||
from django.urls import reverse
|
||||
|
||||
from django.core.signing import Signer
|
||||
|
||||
from umap.models import DataLayer, Map, Star
|
||||
|
||||
from .base import login_required
|
||||
|
@ -16,50 +17,45 @@ User = get_user_model()
|
|||
@pytest.fixture
|
||||
def post_data():
|
||||
return {
|
||||
'name': 'name',
|
||||
'center': '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
|
||||
'settings': '{"type":"Feature","geometry":{"type":"Point","coordinates":[5.0592041015625,52.05924589011585]},"properties":{"tilelayer":{"maxZoom":20,"url_template":"http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png","minZoom":0,"attribution":"HOT and friends"},"licence":"","description":"","name":"test enrhûmé","tilelayersControl":true,"displayDataBrowserOnLoad":false,"displayPopupFooter":true,"displayCaptionOnLoad":false,"miniMap":true,"moreControl":true,"scaleControl":true,"zoomControl":true,"datalayersControl":true,"zoom":8}}' # noqa
|
||||
"name": "name",
|
||||
"center": '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
|
||||
"settings": '{"type":"Feature","geometry":{"type":"Point","coordinates":[5.0592041015625,52.05924589011585]},"properties":{"tilelayer":{"maxZoom":20,"url_template":"http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png","minZoom":0,"attribution":"HOT and friends"},"licence":"","description":"","name":"test enrhûmé","tilelayersControl":true,"displayDataBrowserOnLoad":false,"displayPopupFooter":true,"displayCaptionOnLoad":false,"miniMap":true,"moreControl":true,"scaleControl":true,"zoomControl":true,"datalayersControl":true,"zoom":8}}', # noqa
|
||||
}
|
||||
|
||||
|
||||
def test_create(client, user, post_data):
|
||||
url = reverse('map_create')
|
||||
url = reverse("map_create")
|
||||
# POST only mendatory fields
|
||||
name = 'test-map-with-new-name'
|
||||
post_data['name'] = name
|
||||
name = "test-map-with-new-name"
|
||||
post_data["name"] = name
|
||||
client.login(username=user.username, password="123123")
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 200
|
||||
j = json.loads(response.content.decode())
|
||||
created_map = Map.objects.latest('pk')
|
||||
assert j['id'] == created_map.pk
|
||||
created_map = Map.objects.latest("pk")
|
||||
assert j["id"] == created_map.pk
|
||||
assert created_map.name == name
|
||||
assert created_map.center.x == 13.447265624999998
|
||||
assert created_map.center.y == 48.94415123418794
|
||||
assert j['permissions'] == {
|
||||
'edit_status': 3,
|
||||
'share_status': 1,
|
||||
'owner': {
|
||||
'id': user.pk,
|
||||
'name': 'Joe',
|
||||
'url': '/en/user/Joe/'
|
||||
},
|
||||
'editors': [],
|
||||
'anonymous_edit_url': ('http://umap.org'
|
||||
+ created_map.get_anonymous_edit_url())
|
||||
assert j["permissions"] == {
|
||||
"edit_status": 3,
|
||||
"share_status": 1,
|
||||
"owner": {"id": user.pk, "name": "Joe", "url": "/en/user/Joe/"},
|
||||
"editors": [],
|
||||
"anonymous_edit_url": created_map.get_anonymous_edit_url(),
|
||||
}
|
||||
|
||||
|
||||
def test_map_create_permissions(client, settings):
|
||||
settings.UMAP_ALLOW_ANONYMOUS = False
|
||||
url = reverse('map_create')
|
||||
url = reverse("map_create")
|
||||
# POST anonymous
|
||||
response = client.post(url, {})
|
||||
assert login_required(response)
|
||||
|
||||
|
||||
def test_map_update_access(client, map, user):
|
||||
url = reverse('map_update', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_update", kwargs={"map_id": map.pk})
|
||||
# GET anonymous
|
||||
response = client.get(url)
|
||||
assert login_required(response)
|
||||
|
@ -77,7 +73,7 @@ def test_map_update_access(client, map, user):
|
|||
|
||||
|
||||
def test_map_update_permissions_access(client, map, user):
|
||||
url = reverse('map_update_permissions', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_update_permissions", kwargs={"map_id": map.pk})
|
||||
# GET anonymous
|
||||
response = client.get(url)
|
||||
assert login_required(response)
|
||||
|
@ -95,22 +91,22 @@ def test_map_update_permissions_access(client, map, user):
|
|||
|
||||
|
||||
def test_update(client, map, post_data):
|
||||
url = reverse('map_update', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_update", kwargs={"map_id": map.pk})
|
||||
# POST only mendatory fields
|
||||
name = 'new map name'
|
||||
post_data['name'] = name
|
||||
name = "new map name"
|
||||
post_data["name"] = name
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 200
|
||||
j = json.loads(response.content.decode())
|
||||
assert 'html' not in j
|
||||
assert "html" not in j
|
||||
updated_map = Map.objects.get(pk=map.pk)
|
||||
assert j['id'] == updated_map.pk
|
||||
assert j["id"] == updated_map.pk
|
||||
assert updated_map.name == name
|
||||
|
||||
|
||||
def test_delete(client, map, datalayer):
|
||||
url = reverse('map_delete', args=(map.pk, ))
|
||||
url = reverse("map_delete", args=(map.pk,))
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
response = client.post(url, {}, follow=True)
|
||||
assert response.status_code == 200
|
||||
|
@ -120,61 +116,58 @@ def test_delete(client, map, datalayer):
|
|||
assert User.objects.filter(pk=map.owner.pk).exists()
|
||||
# Test response is a json
|
||||
j = json.loads(response.content.decode())
|
||||
assert 'redirect' in j
|
||||
assert "redirect" in j
|
||||
|
||||
|
||||
def test_wrong_slug_should_redirect_to_canonical(client, map):
|
||||
url = reverse('map', kwargs={'pk': map.pk, 'slug': 'wrong-slug'})
|
||||
canonical = reverse('map', kwargs={'pk': map.pk,
|
||||
'slug': map.slug})
|
||||
url = reverse("map", kwargs={"pk": map.pk, "slug": "wrong-slug"})
|
||||
canonical = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
||||
response = client.get(url)
|
||||
assert response.status_code == 301
|
||||
assert response['Location'] == canonical
|
||||
assert response["Location"] == canonical
|
||||
|
||||
|
||||
def test_wrong_slug_should_redirect_with_query_string(client, map):
|
||||
url = reverse('map', kwargs={'pk': map.pk, 'slug': 'wrong-slug'})
|
||||
url = reverse("map", kwargs={"pk": map.pk, "slug": "wrong-slug"})
|
||||
url = "{}?allowEdit=0".format(url)
|
||||
canonical = reverse('map', kwargs={'pk': map.pk,
|
||||
'slug': map.slug})
|
||||
canonical = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
||||
canonical = "{}?allowEdit=0".format(canonical)
|
||||
response = client.get(url)
|
||||
assert response.status_code == 301
|
||||
assert response['Location'] == canonical
|
||||
assert response["Location"] == canonical
|
||||
|
||||
|
||||
def test_should_not_consider_the_query_string_for_canonical_check(client, map):
|
||||
url = reverse('map', kwargs={'pk': map.pk, 'slug': map.slug})
|
||||
url = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
||||
url = "{}?allowEdit=0".format(url)
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_short_url_should_redirect_to_canonical(client, map):
|
||||
url = reverse('map_short_url', kwargs={'pk': map.pk})
|
||||
canonical = reverse('map', kwargs={'pk': map.pk,
|
||||
'slug': map.slug})
|
||||
url = reverse("map_short_url", kwargs={"pk": map.pk})
|
||||
canonical = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
||||
response = client.get(url)
|
||||
assert response.status_code == 301
|
||||
assert response['Location'] == canonical
|
||||
assert response["Location"] == canonical
|
||||
|
||||
|
||||
def test_clone_map_should_create_a_new_instance(client, map):
|
||||
assert Map.objects.count() == 1
|
||||
url = reverse('map_clone', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_clone", kwargs={"map_id": map.pk})
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
response = client.post(url)
|
||||
assert response.status_code == 200
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest('pk')
|
||||
clone = Map.objects.latest("pk")
|
||||
assert clone.pk != map.pk
|
||||
assert clone.name == u"Clone of " + map.name
|
||||
assert clone.name == "Clone of " + map.name
|
||||
|
||||
|
||||
def test_user_not_allowed_should_not_clone_map(client, map, user, settings):
|
||||
settings.UMAP_ALLOW_ANONYMOUS = False
|
||||
assert Map.objects.count() == 1
|
||||
url = reverse('map_clone', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_clone", kwargs={"map_id": map.pk})
|
||||
map.edit_status = map.OWNER
|
||||
map.save()
|
||||
response = client.post(url)
|
||||
|
@ -191,7 +184,7 @@ def test_user_not_allowed_should_not_clone_map(client, map, user, settings):
|
|||
|
||||
|
||||
def test_clone_should_set_cloner_as_owner(client, map, user):
|
||||
url = reverse('map_clone', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_clone", kwargs={"map_id": map.pk})
|
||||
map.edit_status = map.EDITORS
|
||||
map.editors.add(user)
|
||||
map.save()
|
||||
|
@ -199,32 +192,32 @@ def test_clone_should_set_cloner_as_owner(client, map, user):
|
|||
response = client.post(url)
|
||||
assert response.status_code == 200
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest('pk')
|
||||
clone = Map.objects.latest("pk")
|
||||
assert clone.pk != map.pk
|
||||
assert clone.name == u"Clone of " + map.name
|
||||
assert clone.name == "Clone of " + map.name
|
||||
assert clone.owner == user
|
||||
|
||||
|
||||
def test_map_creation_should_allow_unicode_names(client, map, post_data):
|
||||
url = reverse('map_create')
|
||||
url = reverse("map_create")
|
||||
# POST only mendatory fields
|
||||
name = u'Академический'
|
||||
post_data['name'] = name
|
||||
name = "Академический"
|
||||
post_data["name"] = name
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 200
|
||||
j = json.loads(response.content.decode())
|
||||
created_map = Map.objects.latest('pk')
|
||||
assert j['id'] == created_map.pk
|
||||
created_map = Map.objects.latest("pk")
|
||||
assert j["id"] == created_map.pk
|
||||
assert created_map.name == name
|
||||
# Lower case of the russian original name
|
||||
# self.assertEqual(created_map.slug, u"академический")
|
||||
# for now we fallback to "map", see unicode_name branch
|
||||
assert created_map.slug == 'map'
|
||||
assert created_map.slug == "map"
|
||||
|
||||
|
||||
def test_anonymous_can_access_map_with_share_status_public(client, map):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.PUBLIC
|
||||
map.save()
|
||||
response = client.get(url)
|
||||
|
@ -232,7 +225,7 @@ def test_anonymous_can_access_map_with_share_status_public(client, map):
|
|||
|
||||
|
||||
def test_anonymous_can_access_map_with_share_status_open(client, map):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.OPEN
|
||||
map.save()
|
||||
response = client.get(url)
|
||||
|
@ -240,7 +233,7 @@ def test_anonymous_can_access_map_with_share_status_open(client, map):
|
|||
|
||||
|
||||
def test_anonymous_cannot_access_map_with_share_status_private(client, map):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.PRIVATE
|
||||
map.save()
|
||||
response = client.get(url)
|
||||
|
@ -248,7 +241,7 @@ def test_anonymous_cannot_access_map_with_share_status_private(client, map):
|
|||
|
||||
|
||||
def test_owner_can_access_map_with_share_status_private(client, map):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.PRIVATE
|
||||
map.save()
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
|
@ -257,7 +250,7 @@ def test_owner_can_access_map_with_share_status_private(client, map):
|
|||
|
||||
|
||||
def test_editors_can_access_map_with_share_status_private(client, map, user):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.PRIVATE
|
||||
map.editors.add(user)
|
||||
map.save()
|
||||
|
@ -267,7 +260,7 @@ def test_editors_can_access_map_with_share_status_private(client, map, user):
|
|||
|
||||
|
||||
def test_anonymous_cannot_access_map_with_share_status_blocked(client, map):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.BLOCKED
|
||||
map.save()
|
||||
response = client.get(url)
|
||||
|
@ -275,7 +268,7 @@ def test_anonymous_cannot_access_map_with_share_status_blocked(client, map):
|
|||
|
||||
|
||||
def test_owner_cannot_access_map_with_share_status_blocked(client, map):
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.BLOCKED
|
||||
map.save()
|
||||
client.login(username=map.owner.username, password="123123")
|
||||
|
@ -283,8 +276,10 @@ def test_owner_cannot_access_map_with_share_status_blocked(client, map):
|
|||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_non_editor_cannot_access_map_if_share_status_private(client, map, user): # noqa
|
||||
url = reverse('map', args=(map.slug, map.pk))
|
||||
def test_non_editor_cannot_access_map_if_share_status_private(
|
||||
client, map, user
|
||||
): # noqa
|
||||
url = reverse("map", args=(map.slug, map.pk))
|
||||
map.share_status = map.PRIVATE
|
||||
map.save()
|
||||
client.login(username=user.username, password="123123")
|
||||
|
@ -293,16 +288,16 @@ def test_non_editor_cannot_access_map_if_share_status_private(client, map, user)
|
|||
|
||||
|
||||
def test_map_geojson_view(client, map):
|
||||
url = reverse('map_geojson', args=(map.pk, ))
|
||||
url = reverse("map_geojson", args=(map.pk,))
|
||||
response = client.get(url)
|
||||
j = json.loads(response.content.decode())
|
||||
assert 'json' in response['content-type']
|
||||
assert 'type' in j
|
||||
assert "json" in response["content-type"]
|
||||
assert "type" in j
|
||||
|
||||
|
||||
def test_only_owner_can_delete(client, map, user):
|
||||
map.editors.add(user)
|
||||
url = reverse('map_delete', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_delete", kwargs={"map_id": map.pk})
|
||||
client.login(username=user.username, password="123123")
|
||||
response = client.post(url, {}, follow=True)
|
||||
assert response.status_code == 403
|
||||
|
@ -312,10 +307,10 @@ def test_map_editors_do_not_see_owner_change_input(client, map, user):
|
|||
map.editors.add(user)
|
||||
map.edit_status = map.EDITORS
|
||||
map.save()
|
||||
url = reverse('map_update_permissions', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_update_permissions", kwargs={"map_id": map.pk})
|
||||
client.login(username=user.username, password="123123")
|
||||
response = client.get(url)
|
||||
assert 'id_owner' not in response
|
||||
assert "id_owner" not in response
|
||||
|
||||
|
||||
def test_logged_in_user_can_edit_map_editable_by_anonymous(client, map, user):
|
||||
|
@ -323,114 +318,116 @@ def test_logged_in_user_can_edit_map_editable_by_anonymous(client, map, user):
|
|||
map.edit_status = map.ANONYMOUS
|
||||
map.save()
|
||||
client.login(username=user.username, password="123123")
|
||||
url = reverse('map_update', kwargs={'map_id': map.pk})
|
||||
new_name = 'this is my new name'
|
||||
url = reverse("map_update", kwargs={"map_id": map.pk})
|
||||
new_name = "this is my new name"
|
||||
data = {
|
||||
'center': '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
|
||||
'name': new_name
|
||||
"center": '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
|
||||
"name": new_name,
|
||||
}
|
||||
response = client.post(url, data)
|
||||
assert response.status_code == 200
|
||||
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):
|
||||
url = reverse('map_create')
|
||||
url = reverse("map_create")
|
||||
# POST only mendatory fields
|
||||
name = 'test-map-with-new-name'
|
||||
post_data['name'] = name
|
||||
name = "test-map-with-new-name"
|
||||
post_data["name"] = name
|
||||
response = cookieclient.post(url, post_data)
|
||||
assert response.status_code == 200
|
||||
j = json.loads(response.content.decode())
|
||||
created_map = Map.objects.latest('pk')
|
||||
assert j['id'] == created_map.pk
|
||||
assert (created_map.get_anonymous_edit_url()
|
||||
in j['permissions']['anonymous_edit_url'])
|
||||
created_map = Map.objects.latest("pk")
|
||||
assert j["id"] == created_map.pk
|
||||
assert (
|
||||
created_map.get_anonymous_edit_url() in j["permissions"]["anonymous_edit_url"]
|
||||
)
|
||||
assert created_map.name == name
|
||||
key, value = created_map.signed_cookie_elements
|
||||
assert key in cookieclient.cookies
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): # noqa
|
||||
url = reverse('map_update', kwargs={'map_id': anonymap.pk})
|
||||
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data): # noqa
|
||||
url = reverse('map_update', kwargs={'map_id': anonymap.pk})
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_anonymous_update_with_cookie_should_work(
|
||||
cookieclient, anonymap, post_data
|
||||
): # noqa
|
||||
url = reverse("map_update", kwargs={"map_id": anonymap.pk})
|
||||
# POST only mendatory fields
|
||||
name = 'new map name'
|
||||
post_data['name'] = name
|
||||
name = "new map name"
|
||||
post_data["name"] = name
|
||||
response = cookieclient.post(url, post_data)
|
||||
assert response.status_code == 200
|
||||
j = json.loads(response.content.decode())
|
||||
updated_map = Map.objects.get(pk=anonymap.pk)
|
||||
assert j['id'] == updated_map.pk
|
||||
assert j["id"] == updated_map.pk
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_anonymous_delete(cookieclient, anonymap):
|
||||
url = reverse('map_delete', args=(anonymap.pk, ))
|
||||
url = reverse("map_delete", args=(anonymap.pk,))
|
||||
response = cookieclient.post(url, {}, follow=True)
|
||||
assert response.status_code == 200
|
||||
assert not Map.objects.filter(pk=anonymap.pk).count()
|
||||
# Test response is a json
|
||||
j = json.loads(response.content.decode())
|
||||
assert 'redirect' in j
|
||||
assert "redirect" in j
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_no_cookie_cant_delete(client, anonymap):
|
||||
url = reverse('map_delete', args=(anonymap.pk, ))
|
||||
url = reverse("map_delete", args=(anonymap.pk,))
|
||||
response = client.post(url, {}, follow=True)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_anonymous_edit_url(cookieclient, anonymap):
|
||||
url = anonymap.get_anonymous_edit_url()
|
||||
canonical = reverse('map', kwargs={'pk': anonymap.pk,
|
||||
'slug': anonymap.slug})
|
||||
canonical = reverse("map", kwargs={"pk": anonymap.pk, "slug": anonymap.slug})
|
||||
response = cookieclient.get(url)
|
||||
assert response.status_code == 302
|
||||
assert response['Location'] == canonical
|
||||
assert response["Location"] == canonical
|
||||
key, value = anonymap.signed_cookie_elements
|
||||
assert key in cookieclient.cookies
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_sha1_anonymous_edit_url(cookieclient, anonymap):
|
||||
signer = Signer(algorithm='sha1')
|
||||
signer = Signer(algorithm="sha1")
|
||||
signature = signer.sign(anonymap.pk)
|
||||
url = reverse('map_anonymous_edit_url', kwargs={'signature': signature})
|
||||
canonical = reverse('map', kwargs={'pk': anonymap.pk,
|
||||
'slug': anonymap.slug})
|
||||
url = reverse("map_anonymous_edit_url", kwargs={"signature": signature})
|
||||
canonical = reverse("map", kwargs={"pk": anonymap.pk, "slug": anonymap.slug})
|
||||
response = cookieclient.get(url)
|
||||
assert response.status_code == 302
|
||||
assert response['Location'] == canonical
|
||||
assert response["Location"] == canonical
|
||||
key, value = anonymap.signed_cookie_elements
|
||||
assert key in cookieclient.cookies
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_bad_anonymous_edit_url_should_return_403(cookieclient, anonymap):
|
||||
url = anonymap.get_anonymous_edit_url()
|
||||
url = reverse(
|
||||
'map_anonymous_edit_url',
|
||||
kwargs={'signature': "%s:badsignature" % anonymap.pk}
|
||||
"map_anonymous_edit_url", kwargs={"signature": "%s:badsignature" % anonymap.pk}
|
||||
)
|
||||
response = cookieclient.get(url)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(client, anonymap, user): # noqa
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(
|
||||
client, anonymap, user
|
||||
): # noqa
|
||||
assert Map.objects.count() == 1
|
||||
url = reverse('map_clone', kwargs={'map_id': anonymap.pk})
|
||||
url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
|
||||
anonymap.edit_status = anonymap.OWNER
|
||||
anonymap.save()
|
||||
response = client.post(url)
|
||||
|
@ -441,24 +438,26 @@ def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(clien
|
|||
assert Map.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap): # noqa
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(
|
||||
client, anonymap
|
||||
): # noqa
|
||||
assert Map.objects.count() == 1
|
||||
url = reverse('map_clone', kwargs={'map_id': anonymap.pk})
|
||||
url = reverse("map_clone", kwargs={"map_id": anonymap.pk})
|
||||
anonymap.edit_status = anonymap.ANONYMOUS
|
||||
anonymap.save()
|
||||
response = client.post(url)
|
||||
assert response.status_code == 200
|
||||
assert Map.objects.count() == 2
|
||||
clone = Map.objects.latest('pk')
|
||||
clone = Map.objects.latest("pk")
|
||||
assert clone.pk != anonymap.pk
|
||||
assert clone.name == 'Clone of ' + anonymap.name
|
||||
assert clone.name == "Clone of " + anonymap.name
|
||||
assert clone.owner is None
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_anyone_can_access_anonymous_map(cookieclient, anonymap):
|
||||
url = reverse('map', args=(anonymap.slug, anonymap.pk))
|
||||
url = reverse("map", args=(anonymap.slug, anonymap.pk))
|
||||
anonymap.share_status = anonymap.PUBLIC
|
||||
response = cookieclient.get(url)
|
||||
assert response.status_code == 200
|
||||
|
@ -470,9 +469,9 @@ def test_anyone_can_access_anonymous_map(cookieclient, anonymap):
|
|||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_map_attach_owner(cookieclient, anonymap, user):
|
||||
url = reverse('map_attach_owner', kwargs={'map_id': anonymap.pk})
|
||||
url = reverse("map_attach_owner", kwargs={"map_id": anonymap.pk})
|
||||
cookieclient.login(username=user.username, password="123123")
|
||||
assert anonymap.owner is None
|
||||
response = cookieclient.post(url)
|
||||
|
@ -481,17 +480,17 @@ def test_map_attach_owner(cookieclient, anonymap, user):
|
|||
assert map.owner == user
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_map_attach_owner_not_logged_in(cookieclient, anonymap, user):
|
||||
url = reverse('map_attach_owner', kwargs={'map_id': anonymap.pk})
|
||||
url = reverse("map_attach_owner", kwargs={"map_id": anonymap.pk})
|
||||
assert anonymap.owner is None
|
||||
response = cookieclient.post(url)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('allow_anonymous')
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_map_attach_owner_with_already_an_owner(cookieclient, map, user):
|
||||
url = reverse('map_attach_owner', kwargs={'map_id': map.pk})
|
||||
url = reverse("map_attach_owner", kwargs={"map_id": map.pk})
|
||||
cookieclient.login(username=user.username, password="123123")
|
||||
assert map.owner
|
||||
assert map.owner != user
|
||||
|
@ -500,7 +499,7 @@ def test_map_attach_owner_with_already_an_owner(cookieclient, map, user):
|
|||
|
||||
|
||||
def test_map_attach_owner_anonymous_not_allowed(cookieclient, anonymap, user):
|
||||
url = reverse('map_attach_owner', kwargs={'map_id': anonymap.pk})
|
||||
url = reverse("map_attach_owner", kwargs={"map_id": anonymap.pk})
|
||||
cookieclient.login(username=user.username, password="123123")
|
||||
assert anonymap.owner is None
|
||||
response = cookieclient.post(url)
|
||||
|
@ -524,11 +523,11 @@ def test_map_attach_owner_anonymous_not_allowed(cookieclient, anonymap, user):
|
|||
|
||||
def test_create_readonly(client, user, post_data, settings):
|
||||
settings.UMAP_READONLY = True
|
||||
url = reverse('map_create')
|
||||
url = reverse("map_create")
|
||||
client.login(username=user.username, password="123123")
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 403
|
||||
assert response.content == b'Site is readonly for maintenance'
|
||||
assert response.content == b"Site is readonly for maintenance"
|
||||
|
||||
|
||||
def test_search(client, map):
|
||||
|
@ -542,7 +541,7 @@ def test_search(client, map):
|
|||
|
||||
|
||||
def test_authenticated_user_can_star_map(client, map, user):
|
||||
url = reverse('map_star', args=(map.pk,))
|
||||
url = reverse("map_star", args=(map.pk,))
|
||||
client.login(username=user.username, password="123123")
|
||||
assert Star.objects.filter(by=user).count() == 0
|
||||
response = client.post(url)
|
||||
|
@ -551,7 +550,7 @@ def test_authenticated_user_can_star_map(client, map, user):
|
|||
|
||||
|
||||
def test_anonymous_cannot_star_map(client, map):
|
||||
url = reverse('map_star', args=(map.pk,))
|
||||
url = reverse("map_star", args=(map.pk,))
|
||||
assert Star.objects.count() == 0
|
||||
response = client.post(url)
|
||||
assert response.status_code == 302
|
||||
|
@ -560,12 +559,41 @@ def test_anonymous_cannot_star_map(client, map):
|
|||
|
||||
|
||||
def test_user_can_see_their_star(client, map, user):
|
||||
url = reverse('map_star', args=(map.pk,))
|
||||
url = reverse("map_star", args=(map.pk,))
|
||||
client.login(username=user.username, password="123123")
|
||||
assert Star.objects.filter(by=user).count() == 0
|
||||
response = client.post(url)
|
||||
assert response.status_code == 200
|
||||
url = reverse('user_stars', args=(user.username,))
|
||||
url = reverse("user_stars", args=(user.username,))
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert map.name in response.content.decode()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_cannot_send_link_on_owned_map(client, map):
|
||||
assert len(mail.outbox) == 0
|
||||
url = reverse("map_send_edit_link", args=(map.pk,))
|
||||
resp = client.post(url, {"email": "foo@bar.org"})
|
||||
assert resp.status_code == 200
|
||||
assert json.loads(resp.content.decode()) == {"login_required": "/en/login/"}
|
||||
assert len(mail.outbox) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_cannot_send_link_on_anonymous_map_without_cookie(client, anonymap):
|
||||
assert len(mail.outbox) == 0
|
||||
url = reverse("map_send_edit_link", args=(anonymap.pk,))
|
||||
resp = client.post(url, {"email": "foo@bar.org"})
|
||||
assert resp.status_code == 403
|
||||
assert len(mail.outbox) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("allow_anonymous")
|
||||
def test_can_send_link_on_anonymous_map_with_cookie(cookieclient, anonymap):
|
||||
assert len(mail.outbox) == 0
|
||||
url = reverse("map_send_edit_link", args=(anonymap.pk,))
|
||||
resp = cookieclient.post(url, {"email": "foo@bar.org"})
|
||||
assert resp.status_code == 200
|
||||
assert len(mail.outbox) == 1
|
||||
assert mail.outbox[0].subject == "The uMap edit link for your map: test map"
|
||||
|
|
14
umap/urls.py
14
umap/urls.py
|
@ -99,8 +99,7 @@ i18n_urls += decorated_patterns(
|
|||
name="map_star",
|
||||
),
|
||||
)
|
||||
i18n_urls += decorated_patterns(
|
||||
[map_permissions_check, never_cache],
|
||||
map_urls = [
|
||||
re_path(
|
||||
r"^map/(?P<map_id>[\d]+)/update/settings/$",
|
||||
views.MapUpdate.as_view(),
|
||||
|
@ -141,7 +140,16 @@ i18n_urls += decorated_patterns(
|
|||
views.DataLayerDelete.as_view(),
|
||||
name="datalayer_delete",
|
||||
),
|
||||
)
|
||||
]
|
||||
if settings.FROM_EMAIL:
|
||||
map_urls.append(
|
||||
re_path(
|
||||
r"^map/(?P<map_id>[\d]+)/send-edit-link/$",
|
||||
views.SendEditLink.as_view(),
|
||||
name="map_send_edit_link",
|
||||
)
|
||||
)
|
||||
i18n_urls += decorated_patterns([map_permissions_check, never_cache], *map_urls)
|
||||
urlpatterns += i18n_patterns(
|
||||
re_path(r"^$", views.home, name="home"),
|
||||
re_path(
|
||||
|
|
|
@ -14,6 +14,7 @@ from django.contrib.auth import logout as do_logout
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.gis.measure import D
|
||||
from django.contrib.postgres.search import SearchQuery, SearchVector
|
||||
from django.core.mail import send_mail
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||
from django.core.signing import BadSignature, Signer
|
||||
from django.core.validators import URLValidator, ValidationError
|
||||
|
@ -35,7 +36,7 @@ from django.utils.translation import to_locale
|
|||
from django.views.generic import DetailView, TemplateView, View
|
||||
from django.views.generic.base import RedirectView
|
||||
from django.views.generic.detail import BaseDetailView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from .forms import (
|
||||
|
@ -46,6 +47,7 @@ from .forms import (
|
|||
DataLayerForm,
|
||||
FlatErrorList,
|
||||
MapSettingsForm,
|
||||
SendLinkForm,
|
||||
UpdateMapPermissionsForm,
|
||||
)
|
||||
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
|
||||
|
@ -472,13 +474,9 @@ class PermissionsMixin:
|
|||
for editor in self.object.editors.all()
|
||||
]
|
||||
if not self.object.owner and self.object.is_anonymous_owner(self.request):
|
||||
permissions["anonymous_edit_url"] = self.get_anonymous_edit_url()
|
||||
permissions["anonymous_edit_url"] = self.object.get_anonymous_edit_url()
|
||||
return permissions
|
||||
|
||||
def get_anonymous_edit_url(self):
|
||||
anonymous_url = self.object.get_anonymous_edit_url()
|
||||
return settings.SITE_URL + anonymous_url
|
||||
|
||||
|
||||
class MapView(MapDetailMixin, PermissionsMixin, DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -547,15 +545,7 @@ class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView):
|
|||
if self.request.user.is_authenticated:
|
||||
form.instance.owner = self.request.user
|
||||
self.object = form.save()
|
||||
anonymous_url = self.get_anonymous_edit_url()
|
||||
if not self.request.user.is_authenticated:
|
||||
msg = _(
|
||||
"Your map has been created! If you want to edit this map from "
|
||||
"another computer, please use this link: %(anonymous_url)s"
|
||||
% {"anonymous_url": anonymous_url}
|
||||
)
|
||||
else:
|
||||
msg = _("Congratulations, your map has been created!")
|
||||
anonymous_url = self.object.get_anonymous_edit_url()
|
||||
permissions = self.get_permissions()
|
||||
# User does not have the cookie yet.
|
||||
permissions["anonymous_edit_url"] = anonymous_url
|
||||
|
@ -563,7 +553,6 @@ class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView):
|
|||
id=self.object.pk,
|
||||
url=self.object.get_absolute_url(),
|
||||
permissions=permissions,
|
||||
info=msg,
|
||||
)
|
||||
if not self.request.user.is_authenticated:
|
||||
key, value = self.object.signed_cookie_elements
|
||||
|
@ -628,6 +617,36 @@ class AttachAnonymousMap(View):
|
|||
return simple_json_response()
|
||||
|
||||
|
||||
class SendEditLink(FormLessEditMixin, FormView):
|
||||
form_class = SendLinkForm
|
||||
|
||||
def post(self, form, **kwargs):
|
||||
self.object = kwargs["map_inst"]
|
||||
if (
|
||||
self.object.owner
|
||||
or not self.object.is_anonymous_owner(self.request)
|
||||
or not self.object.can_edit(self.request.user, self.request)
|
||||
):
|
||||
return HttpResponseForbidden()
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
email = form.cleaned_data["email"]
|
||||
else:
|
||||
return HttpResponseBadRequest("Invalid")
|
||||
link = self.object.get_anonymous_edit_url()
|
||||
|
||||
send_mail(
|
||||
_("The uMap edit link for your map: %(map_name)s" % {"map_name": self.object.name}),
|
||||
_("Here is your secret edit link: %(link)s" % {"link": link}),
|
||||
settings.FROM_EMAIL,
|
||||
[email],
|
||||
fail_silently=False,
|
||||
)
|
||||
return simple_json_response(
|
||||
info=_("Email sent to %(email)s" % {"email": email})
|
||||
)
|
||||
|
||||
|
||||
class MapDelete(DeleteView):
|
||||
model = Map
|
||||
pk_url_kwarg = "map_id"
|
||||
|
@ -660,7 +679,7 @@ class MapClone(PermissionsMixin, View):
|
|||
msg = _(
|
||||
"Your map has been cloned! If you want to edit this map from "
|
||||
"another computer, please use this link: %(anonymous_url)s"
|
||||
% {"anonymous_url": self.get_anonymous_edit_url()}
|
||||
% {"anonymous_url": self.object.get_anonymous_edit_url()}
|
||||
)
|
||||
else:
|
||||
msg = _("Congratulations, your map has been cloned!")
|
||||
|
|
Loading…
Reference in a new issue