feat(members/waitinglist): add leave waitinglist link to reminders

pull/136/head
Christian Merten 10 months ago
parent 1d2f0c152f
commit 4c4b450245
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -77,6 +77,10 @@ Wenn du weiterhin auf der Warteliste bleiben möchtest, klicke auf den folgenden
{link} {link}
Falls du kein Interesse mehr hast, kannst du unter folgendem Link die Warteliste verlassen:
{leave_link}
Das ist Erinnerung Nummer {reminder} von {max_reminder_count}. Nach Erinnerung Nummer {max_reminder_count} wirst Das ist Erinnerung Nummer {reminder} von {max_reminder_count}. Nach Erinnerung Nummer {max_reminder_count} wirst
du automatisch entfernt. du automatisch entfernt.

@ -81,6 +81,10 @@ def get_wait_confirmation_link(waiter):
return prepend_base_url("/members/waitinglist/confirm?key={}".format(key)) return prepend_base_url("/members/waitinglist/confirm?key={}".format(key))
def get_leave_waitinglist_link(key):
return prepend_base_url("/members/waitinglist/leave?key={}".format(key))
def get_mail_confirmation_link(key): def get_mail_confirmation_link(key):
return prepend_base_url("/members/mail/confirm?key={}".format(key)) return prepend_base_url("/members/mail/confirm?key={}".format(key))

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-08 00:12+0100\n" "POT-Creation-Date: 2025-02-11 23:51+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -1713,6 +1713,38 @@ msgstr "Registrierung fehlgeschlagen"
msgid "Registration" msgid "Registration"
msgstr "Registrierung" msgstr "Registrierung"
#: members/templates/members/leave_waitinglist.html
msgid "Leave waitinglist"
msgstr "Warteliste verlassen"
#: members/templates/members/leave_waitinglist.html
msgid ""
"Are you sure you want to leave the waitinglist? This can't be undone and\n"
"you need to requeue if you change your mind later."
msgstr ""
"Bist du sicher, dass du die Warteliste verlassen möchtest? Dies kann nicht "
"rückgängig gemacht werden. Falls du später wieder der Warteliste beitreten möchtest, "
"musst du dich neu registrieren."
#: members/templates/members/leave_waitinglist.html
msgid "Yes, leave the waitinglist"
msgstr "Ja, Warteliste verlassen"
#: members/templates/members/leave_waitinglist_success.html
msgid "Left waitinglist"
msgstr "Warteliste verlassen"
#: members/templates/members/leave_waitinglist_success.html
#: members/templates/members/reject_success.html
msgid ""
"You successfully unregistered from the waitinglist. If you want to rejoin "
"the waitinglist\n"
"at a later time, please do so on our website.\n"
msgstr ""
"Du hast dich erfolgreich von der Warteliste abgemeldet. Falls du dich zu "
"einem späteren Zeitpunkt wieder auf die Warteliste setzen lassen möchtest "
"kannst du das auf unserer Webseite machen.\n"
#: members/templates/members/mail_confirmation_invalid.html members/tests.py #: members/templates/members/mail_confirmation_invalid.html members/tests.py
msgid "Mail confirmation failed" msgid "Mail confirmation failed"
msgstr "Emailbestätigung fehlgeschlagen" msgstr "Emailbestätigung fehlgeschlagen"
@ -1908,16 +1940,6 @@ msgstr "Diese Einladung ablehnen und auf der Warteliste bleiben"
msgid "Leave the waitinglist" msgid "Leave the waitinglist"
msgstr "Warteliste verlassen" msgstr "Warteliste verlassen"
#: members/templates/members/reject_success.html
msgid ""
"You successfully unregistered from the waitinglist. If you want to rejoin "
"the waitinglist\n"
"at a later time, please do so on our website.\n"
msgstr ""
"Du hast dich erfolgreich von der Warteliste abgemeldet. Falls du dich zu "
"einem späteren Zeitpunkt wieder auf die Warteliste setzen lassen möchtest "
"kannst du das auf unserer Webseite machen.\n"
#: members/templates/members/reject_success.html #: members/templates/members/reject_success.html
#, python-format #, python-format
msgid "" msgid ""

@ -0,0 +1,18 @@
# Generated by Django 4.0.1 on 2025-02-11 22:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0036_excursion_add_approval_status'),
]
operations = [
migrations.AddField(
model_name='memberwaitinglist',
name='leave_key',
field=models.CharField(default='', max_length=32),
),
]

@ -18,7 +18,7 @@ from utils import RestrictedFileField
import os import os
from mailer.mailutils import send as send_mail, get_mail_confirmation_link,\ from mailer.mailutils import send as send_mail, get_mail_confirmation_link,\
prepend_base_url, get_registration_link, get_wait_confirmation_link,\ prepend_base_url, get_registration_link, get_wait_confirmation_link,\
get_invitation_reject_link, get_invite_as_user_key get_invitation_reject_link, get_invite_as_user_key, get_leave_waitinglist_link
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.conf import settings from django.conf import settings
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
@ -894,6 +894,8 @@ class MemberWaitingList(Person):
wait_confirmation_key = models.CharField(max_length=32, default="") wait_confirmation_key = models.CharField(max_length=32, default="")
wait_confirmation_key_expire = models.DateTimeField(default=timezone.now) wait_confirmation_key_expire = models.DateTimeField(default=timezone.now)
leave_key = models.CharField(max_length=32, default="")
last_reminder = models.DateTimeField(auto_now=True, verbose_name=_('Last reminder')) last_reminder = models.DateTimeField(auto_now=True, verbose_name=_('Last reminder'))
sent_reminders = models.IntegerField(default=0, verbose_name=_('Missed reminders')) sent_reminders = models.IntegerField(default=0, verbose_name=_('Missed reminders'))
@ -940,10 +942,12 @@ class MemberWaitingList(Person):
"""Sends an email to the person asking them to confirm their intention to wait.""" """Sends an email to the person asking them to confirm their intention to wait."""
self.last_reminder = datetime.now() self.last_reminder = datetime.now()
self.sent_reminders += 1 self.sent_reminders += 1
self.leave_key = gen_key()
self.save() self.save()
self.send_mail(_('Waiting confirmation needed'), self.send_mail(_('Waiting confirmation needed'),
settings.WAIT_CONFIRMATION_TEXT.format(name=self.prename, settings.WAIT_CONFIRMATION_TEXT.format(name=self.prename,
link=get_wait_confirmation_link(self), link=get_wait_confirmation_link(self),
leave_link=get_leave_waitinglist_link(self.leave_key),
reminder=self.sent_reminders, reminder=self.sent_reminders,
max_reminder_count=settings.MAX_REMINDER_COUNT)) max_reminder_count=settings.MAX_REMINDER_COUNT))
@ -957,6 +961,7 @@ class MemberWaitingList(Person):
self.last_wait_confirmation = timezone.now() self.last_wait_confirmation = timezone.now()
self.wait_confirmation_key_expire = timezone.now() self.wait_confirmation_key_expire = timezone.now()
self.sent_reminders = 0 self.sent_reminders = 0
self.leave_key = ''
self.save() self.save()
return self.WAITING_CONFIRMATION_SUCCESS return self.WAITING_CONFIRMATION_SUCCESS

@ -0,0 +1,25 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Leave waitinglist" %}
{% endblock %}
{% block content %}
<h1>{% trans "Leave waitinglist" %}</h1>
<p>{% blocktrans %}Are you sure you want to leave the waitinglist? This can't be undone and
you need to requeue if you change your mind later.{% endblocktrans %}
</p>
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="key" value="{{waiter.leave_key}}">
<p>
<input type="submit" name="leave_waitinglist" value="{% trans "Yes, leave the waitinglist" %}"/>
</p>
</form>
{% endblock %}

@ -0,0 +1,19 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Left waitinglist" %}
{% endblock %}
{% block content %}
<h1>{% trans "Left waitinglist" %}</h1>
<p>
{% blocktrans %}You successfully unregistered from the waitinglist. If you want to rejoin the waitinglist
at a later time, please do so on our website.
{% endblocktrans %}
</p>
{% endblock %}

@ -1470,6 +1470,8 @@ class ConfirmWaitingViewTestCase(BasicMemberTestCase):
response = self.client.get(url, data={'key': self.key, 'foo': 'bar'}) response = self.client.get(url, data={'key': self.key, 'foo': 'bar'})
self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('Waiting confirmed')) self.assertContains(response, _('Waiting confirmed'))
waiter = MemberWaitingList.objects.get(pk=self.waiter.pk)
self.assertEqual(waiter.leave_key, '')
@skip("This currently fails, because `last_wait_confirmation` has `auto_now=True`, which is wrong.") @skip("This currently fails, because `last_wait_confirmation` has `auto_now=True`, which is wrong.")
def test_get_expired(self): def test_get_expired(self):
@ -1486,6 +1488,34 @@ class ConfirmWaitingViewTestCase(BasicMemberTestCase):
self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('rejoin the waiting list')) self.assertContains(response, _('rejoin the waiting list'))
def test_get_leave(self):
url = reverse('members:leave_waitinglist')
response = self.client.get(url, data={'key': self.waiter.leave_key})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('Leave waitinglist'))
# modify the POST data, otherwise the request is cached
response = self.client.post(url, data={'key': self.waiter.leave_key,
'leave_waitinglist': 'bar'})
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(response, _('Left waitinglist'))
self.assertRaises(MemberWaitingList.DoesNotExist, MemberWaitingList.objects.get, pk=self.waiter.pk)
def test_leave_invalid(self):
url = reverse('members:leave_waitinglist')
# get, wrong key
response = self.client.get(url, data={'key': 'foobar'})
self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
# post, wrong key
response = self.client.post(url, data={'key': 'foobar'})
self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
# post, no key
response = self.client.post(url)
self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
# post, no sanity flag
response = self.client.post(url, data={'key': self.waiter.leave_key})
self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
class MailConfirmationViewTestCase(BasicMemberTestCase): class MailConfirmationViewTestCase(BasicMemberTestCase):
def setUp(self): def setUp(self):

@ -10,6 +10,7 @@ urlpatterns = [
re_path(r'^register/upload', views.upload_registration_form , name='upload_registration_form'), re_path(r'^register/upload', views.upload_registration_form , name='upload_registration_form'),
re_path(r'^register', views.register , name='register'), re_path(r'^register', views.register , name='register'),
re_path(r'^waitinglist/confirm', views.confirm_waiting , name='confirm_waiting'), re_path(r'^waitinglist/confirm', views.confirm_waiting , name='confirm_waiting'),
re_path(r'^waitinglist/leave', views.leave_waitinglist , name='leave_waitinglist'),
re_path(r'^waitinglist/invitation/reject', views.reject_invitation , name='reject_invitation'), re_path(r'^waitinglist/invitation/reject', views.reject_invitation , name='reject_invitation'),
re_path(r'^waitinglist', views.register_waiting_list , name='register_waiting_list'), re_path(r'^waitinglist', views.register_waiting_list , name='register_waiting_list'),
re_path(r'^mail/confirm', views.confirm_mail , name='confirm_mail'), re_path(r'^mail/confirm', views.confirm_mail , name='confirm_mail'),

@ -1,6 +1,6 @@
from startpage.views import render from startpage.views import render
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect, Http404
from django import forms from django import forms
from django.forms import ModelForm, TextInput, DateInput, BaseInlineFormSet,\ from django.forms import ModelForm, TextInput, DateInput, BaseInlineFormSet,\
inlineformset_factory, HiddenInput, FileInput inlineformset_factory, HiddenInput, FileInput
@ -512,3 +512,32 @@ def render_waiting_confirmation_invalid(request, prename=None, expired=False):
def render_waiting_confirmation_success(request, prename, already_confirmed): def render_waiting_confirmation_success(request, prename, already_confirmed):
return render(request, 'members/waiting_confirmation_success.html', return render(request, 'members/waiting_confirmation_success.html',
{'prename': prename, 'already_confirmed': already_confirmed}) {'prename': prename, 'already_confirmed': already_confirmed})
def render_leave_waitinglist(request, waiter):
return render(request, 'members/leave_waitinglist.html', dict(waiter=waiter))
def render_leave_waitinglist_success(request, waiter):
return render(request, 'members/leave_waitinglist_success.html', dict(waiter=waiter))
def leave_waitinglist(request):
if request.method == 'GET' and 'key' in request.GET:
key = request.GET['key']
try:
waiter = MemberWaitingList.objects.get(leave_key=key)
return render_leave_waitinglist(request, waiter)
except (MemberWaitingList.DoesNotExist, MemberWaitingList.MultipleObjectsReturned):
raise Http404("Waiter with given leave key does not exist.")
if request.method != 'POST' or 'key' not in request.POST:
raise Http404("Waiter with given leave key does not exist.")
key = request.POST['key']
try:
waiter = MemberWaitingList.objects.get(leave_key=key)
except (MemberWaitingList.DoesNotExist, MemberWaitingList.MultipleObjectsReturned):
raise Http404("Waiter with given leave key does not exist.")
if not 'leave_waitinglist' in request.POST:
raise Http404("leave_waitinglist not found in POST data.")
waiter.unregister()
return render_leave_waitinglist_success(request, waiter)

Loading…
Cancel
Save