diff --git a/jdav_web/jdav_web/settings/components/texts.py b/jdav_web/jdav_web/settings/components/texts.py index 5659344..302ae97 100644 --- a/jdav_web/jdav_web/settings/components/texts.py +++ b/jdav_web/jdav_web/settings/components/texts.py @@ -77,6 +77,10 @@ Wenn du weiterhin auf der Warteliste bleiben möchtest, klicke auf den folgenden {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 du automatisch entfernt. diff --git a/jdav_web/mailer/mailutils.py b/jdav_web/mailer/mailutils.py index b2fe2d9..ba5cc67 100644 --- a/jdav_web/mailer/mailutils.py +++ b/jdav_web/mailer/mailutils.py @@ -81,6 +81,10 @@ def get_wait_confirmation_link(waiter): 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): return prepend_base_url("/members/mail/confirm?key={}".format(key)) diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po index 6eb4fb3..6448d4d 100644 --- a/jdav_web/members/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1713,6 +1713,38 @@ msgstr "Registrierung fehlgeschlagen" msgid "Registration" 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 msgid "Mail confirmation failed" msgstr "Emailbestätigung fehlgeschlagen" @@ -1908,16 +1940,6 @@ msgstr "Diese Einladung ablehnen und auf der Warteliste bleiben" msgid "Leave the waitinglist" 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 #, python-format msgid "" diff --git a/jdav_web/members/migrations/0037_memberwaitinglist_leave_key.py b/jdav_web/members/migrations/0037_memberwaitinglist_leave_key.py new file mode 100644 index 0000000..87d4f08 --- /dev/null +++ b/jdav_web/members/migrations/0037_memberwaitinglist_leave_key.py @@ -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), + ), + ] diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 22d4acf..fda3b41 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -18,7 +18,7 @@ from utils import RestrictedFileField import os from mailer.mailutils import send as send_mail, get_mail_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.conf import settings 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_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')) 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.""" self.last_reminder = datetime.now() self.sent_reminders += 1 + self.leave_key = gen_key() self.save() self.send_mail(_('Waiting confirmation needed'), settings.WAIT_CONFIRMATION_TEXT.format(name=self.prename, link=get_wait_confirmation_link(self), + leave_link=get_leave_waitinglist_link(self.leave_key), reminder=self.sent_reminders, max_reminder_count=settings.MAX_REMINDER_COUNT)) @@ -957,6 +961,7 @@ class MemberWaitingList(Person): self.last_wait_confirmation = timezone.now() self.wait_confirmation_key_expire = timezone.now() self.sent_reminders = 0 + self.leave_key = '' self.save() return self.WAITING_CONFIRMATION_SUCCESS diff --git a/jdav_web/members/templates/members/leave_waitinglist.html b/jdav_web/members/templates/members/leave_waitinglist.html new file mode 100644 index 0000000..7789093 --- /dev/null +++ b/jdav_web/members/templates/members/leave_waitinglist.html @@ -0,0 +1,25 @@ +{% extends "members/base.html" %} +{% load i18n %} +{% load static %} + +{% block title %} +{% trans "Leave waitinglist" %} +{% endblock %} + +{% block content %} + +

{% trans "Leave waitinglist" %}

+ +

{% 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 %} +

+ +
+ {% csrf_token %} + +

+ +

+
+ +{% endblock %} diff --git a/jdav_web/members/templates/members/leave_waitinglist_success.html b/jdav_web/members/templates/members/leave_waitinglist_success.html new file mode 100644 index 0000000..a484efd --- /dev/null +++ b/jdav_web/members/templates/members/leave_waitinglist_success.html @@ -0,0 +1,19 @@ +{% extends "members/base.html" %} +{% load i18n %} +{% load static %} + +{% block title %} +{% trans "Left waitinglist" %} +{% endblock %} + +{% block content %} + +

{% trans "Left waitinglist" %}

+ +

+{% 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 %} +

+ +{% endblock %} diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests.py index 1fd9033..c73a5ef 100644 --- a/jdav_web/members/tests.py +++ b/jdav_web/members/tests.py @@ -1470,6 +1470,8 @@ class ConfirmWaitingViewTestCase(BasicMemberTestCase): response = self.client.get(url, data={'key': self.key, 'foo': 'bar'}) self.assertEqual(response.status_code, HTTPStatus.OK) 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.") def test_get_expired(self): @@ -1486,6 +1488,34 @@ class ConfirmWaitingViewTestCase(BasicMemberTestCase): self.assertEqual(response.status_code, HTTPStatus.OK) 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): def setUp(self): diff --git a/jdav_web/members/urls.py b/jdav_web/members/urls.py index 8e0e773..7390159 100644 --- a/jdav_web/members/urls.py +++ b/jdav_web/members/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ re_path(r'^register/upload', views.upload_registration_form , name='upload_registration_form'), re_path(r'^register', views.register , name='register'), 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', views.register_waiting_list , name='register_waiting_list'), re_path(r'^mail/confirm', views.confirm_mail , name='confirm_mail'), diff --git a/jdav_web/members/views.py b/jdav_web/members/views.py index 2364a0e..6280153 100644 --- a/jdav_web/members/views.py +++ b/jdav_web/members/views.py @@ -1,6 +1,6 @@ from startpage.views import render 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.forms import ModelForm, TextInput, DateInput, BaseInlineFormSet,\ 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): return render(request, 'members/waiting_confirmation_success.html', {'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)