WIP: final bit to make sending edit link working

This commit is contained in:
Yohan Boniface 2023-05-31 17:05:57 +02:00
parent eb32dcc9b6
commit 8f52d34bb2
7 changed files with 87 additions and 28 deletions

View file

@ -114,6 +114,8 @@ INSTALLED_APPS = (
) )
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
# ============================================================================= # =============================================================================
# Calculation of directories relative to the project module location # Calculation of directories relative to the project module location
# ============================================================================= # =============================================================================

View file

@ -748,6 +748,10 @@ input[type=hidden].blur + .button {
.umap-alert .error .umap-action:hover { .umap-alert .error .umap-action:hover {
color: #fff; color: #fff;
} }
.umap-alert input {
padding: 5px;
border-radius: 4px;
}
/* *********** */ /* *********** */
/* Tooltip */ /* Tooltip */

View file

@ -1345,10 +1345,9 @@ L.U.Map.include({
if (data.permissions && data.permissions.anonymous_edit_url) { if (data.permissions && data.permissions.anonymous_edit_url) {
alert.actions = [ alert.actions = [
{ {
label: L._('Send me edit link by email'), label: L._('Send me the link'),
callback: function () { input: L._('Email'),
this.sendEditLink() callback: this.sendEditLink,
},
callbackContext: this, callbackContext: this,
}, },
] ]
@ -1374,8 +1373,16 @@ L.U.Map.include({
}, },
sendEditLink: function () { sendEditLink: function () {
var url = L.Util.template(this.options.urls.map_send_edit_link, { const url = L.Util.template(this.options.urls.map_send_edit_link, {
map_id: this.options.umap_id, 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,
}) })
}, },

View file

@ -75,7 +75,6 @@ L.U.UI = L.Evented.extend({
}, },
popAlert: function (e) { popAlert: function (e) {
const self = this
if (!e) { if (!e) {
if (this.ALERTS.length) e = this.ALERTS.pop() if (this.ALERTS.length) e = this.ALERTS.pop()
else return else return
@ -108,20 +107,25 @@ L.U.UI = L.Evented.extend({
) )
L.DomUtil.add('div', '', this._alert, e.content) L.DomUtil.add('div', '', this._alert, e.content)
if (e.actions) { if (e.actions) {
let action, el let action, el, input
for (let i = 0; i < e.actions.length; i++) { for (let i = 0; i < e.actions.length; i++) {
action = e.actions[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 = L.DomUtil.element('a', { className: 'umap-action' }, this._alert)
el.href = '#' el.href = '#'
el.textContent = action.label el.textContent = action.label
L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', close, this) L.DomEvent.on(el, 'click', L.DomEvent.stop).on(el, 'click', close, this)
if (action.callback) if (action.callback)
L.DomEvent.on( L.DomEvent.on(el, 'click', () => {
el, action.callback.bind(action.callbackContext || this.map)()
'click', close.bind(this)()
action.callback, })
action.callbackContext || this.map
)
} }
} }
if (e.duration !== Infinity) { if (e.duration !== Infinity) {

View file

@ -4,6 +4,8 @@ from umap.settings.base import * # pylint: disable=W0614,W0401
SECRET_KEY = "justfortests" SECRET_KEY = "justfortests"
COMPRESS_ENABLED = False COMPRESS_ENABLED = False
FROM_EMAIL = "test@test.org"
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
if "TRAVIS" in os.environ: if "TRAVIS" in os.environ:
DATABASES = { DATABASES = {

View file

@ -2,9 +2,10 @@ import json
import pytest import pytest
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core import mail
from django.urls import reverse from django.urls import reverse
from django.core.signing import Signer from django.core.signing import Signer
from umap.models import DataLayer, Map, Star from umap.models import DataLayer, Map, Star
from .base import login_required from .base import login_required
@ -568,3 +569,32 @@ def test_user_can_see_their_star(client, map, user):
response = client.get(url) response = client.get(url)
assert response.status_code == 200 assert response.status_code == 200
assert map.name in response.content.decode() 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 == "Your secret edit link"

View file

@ -12,6 +12,7 @@ from django.contrib.auth import logout as do_logout
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.gis.measure import D from django.contrib.gis.measure import D
from django.contrib.postgres.search import SearchQuery, SearchVector 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.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.core.signing import BadSignature, Signer from django.core.signing import BadSignature, Signer
from django.core.validators import URLValidator, ValidationError from django.core.validators import URLValidator, ValidationError
@ -33,7 +34,7 @@ from django.utils.translation import to_locale
from django.views.generic import DetailView, TemplateView, View from django.views.generic import DetailView, TemplateView, View
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.views.generic.detail import BaseDetailView 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 django.views.generic.list import ListView
from .forms import ( from .forms import (
@ -44,6 +45,7 @@ from .forms import (
DataLayerForm, DataLayerForm,
FlatErrorList, FlatErrorList,
MapSettingsForm, MapSettingsForm,
SendLinkForm,
UpdateMapPermissionsForm, UpdateMapPermissionsForm,
) )
from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer from .models import DataLayer, Licence, Map, Pictogram, Star, TileLayer
@ -618,26 +620,34 @@ class AttachAnonymousMap(View):
return simple_json_response() return simple_json_response()
class SendEditLink(FormLessEditMixin, PermissionsMixin, UpdateView): class SendEditLink(FormLessEditMixin, FormView):
model = Map form_class = SendLinkForm
pk_url_kwarg = 'map_id'
def form_valid(self, form): def post(self, form, **kwargs):
if (self.object.owner self.object = kwargs["map_inst"]
if (
self.object.owner
or not self.object.is_anonymous_owner(self.request) or not self.object.is_anonymous_owner(self.request)
or not self.object.can_edit(self.request.user, self.request)): or not self.object.can_edit(self.request.user, self.request)
):
return HttpResponseForbidden() return HttpResponseForbidden()
form = self.get_form()
if form.is_valid():
email = form.cleaned_data["email"] email = form.cleaned_data["email"]
from django.core.mail import send_mail else:
return HttpResponseBadRequest("Invalid")
link = self.object.get_anonymous_edit_url()
send_mail( send_mail(
_('Your secret edit link'), _("Your secret edit link"),
_('Here is your secret edit link: %(link)s' % {"link": link}), _("Here is your secret edit link: %(link)s" % {"link": link}),
settings.FROM_EMAIL, settings.FROM_EMAIL,
[email], [email],
fail_silently=False, fail_silently=False,
) )
return simple_json_response(info=_("Email sent to %(email)s" % {"email": email})) return simple_json_response(
info=_("Email sent to %(email)s" % {"email": email})
)
class MapDelete(DeleteView): class MapDelete(DeleteView):