feat(members/waitinglist): add confirm link in invitation mail and more notifications

MK/finance_workflow
Christian Merten 8 months ago
parent 0535cce70f
commit d913c8049d
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -40,6 +40,28 @@ aber weiterhin auf der Warteliste.
Viele Grüße
Dein KOMPASS""")
GROUP_INVITATION_CONFIRMED_TEXT = get_text('group_invitation_confirmed',
default="""Hallo {name},
{waiter} hat die Einladung zu einer Schnupperstunde bei der Gruppe {group} angenommen.
Viele Grüße
Dein KOMPASS""")
TRIAL_GROUP_MEETING_CONFIRMED_TEXT = get_text('trial_group_meeting_confirmed',
default="""Hallo {name},
deine Teilnahme an der Schnupperstunde der Gruppe {group} wurde erfolgreich bestätigt.
{timeinfo}
Für alle weiteren Absprachen, kontaktiere bitte die Jugendleiter*innen der Gruppe
unter {contact_email}.
Viele Grüße
Deine JDAV %(SEKTION)s""" % { 'SEKTION': SEKTION })
GROUP_TIME_AVAILABLE_TEXT = get_text('group_time_available',
default="""Die Gruppenstunde findet jeden {weekday} von {start_time} bis {end_time} Uhr statt.""")
@ -51,7 +73,11 @@ INVITE_TEXT = get_text('invite', default="""Hallo {{name}},
wir haben gute Neuigkeiten für dich. Es ist ein Platz in der Jugendgruppe {group_name} {group_link}freigeworden.
{group_time}
Bitte kontaktiere die Gruppenleitung ({contact_email}) für alle weiteren Absprachen.
Wenn du an der Schnupperstunde teilnehmen möchtest, bestätige deine Teilnahme bitte unter folgendem Link:
{{invitation_confirm_link}}
Für alle weiteren Absprachen, kontaktiere bitte die Gruppenleitung ({contact_email}).
Wenn du nach der Schnupperstunde beschließt der Gruppe beizutreten, benötigen wir noch ein paar
Informationen und eine schriftliche Anmeldebestätigung von dir. Das kannst du alles über folgenden Link erledigen:

@ -76,6 +76,10 @@ def get_invitation_reject_link(key):
return prepend_base_url("/members/waitinglist/invitation/reject?key={}".format(key))
def get_invitation_confirm_link(key):
return prepend_base_url("/members/waitinglist/invitation/confirm?key={}".format(key))
def get_wait_confirmation_link(waiter):
key = waiter.generate_wait_confirmation_key()
return prepend_base_url("/members/waitinglist/confirm?key={}".format(key))

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-06 14:02+0200\n"
"POT-Creation-Date: 2025-04-06 15:38+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -805,6 +805,15 @@ msgstr "%(waiter)s hat die Warteliste verlassen"
msgid "Group invitation rejected by %(waiter)s"
msgstr "Einladung zur Schnupperstunde von %(waiter)s abgelehnt"
#: members/models.py
#, python-format
msgid "Group invitation confirmed by %(waiter)s"
msgstr "Teilnahme an Schnupperstunde von %(waiter)s bestätigt"
#: members/models.py
msgid "Trial group meeting confirmed"
msgstr "Teilnahme an Schnupperstunde bestätigt"
#: members/models.py
msgid "Do you want to tell us something else?"
msgstr "Möchtest du uns noch etwas mitteilen?"
@ -1655,6 +1664,60 @@ msgstr "Fähigkeitsniveau"
msgid "Save and confirm registration"
msgstr "Speichern und Registrierung bestätigen"
#: members/templates/members/confirm_invalid.html
msgid "Confirm invitation"
msgstr "Teilnahme bestätigen"
#: members/templates/members/confirm_invalid.html
#: members/templates/members/reject_invalid.html members/tests.py
msgid "This invitation is invalid or expired."
msgstr "Diese Einladung ist ungültig oder abgelaufen."
#: members/templates/members/confirm_invitation.html
msgid "Confirm trial group meeting invitation"
msgstr "Teilnahme bestätigen"
#: members/templates/members/confirm_invitation.html
#, python-format
msgid "You were invited to a trial group meeting of the group %(groupname)s."
msgstr ""
"Du wurdest zu einer Schnupperstunde in der Gruppe %(groupname)s eingeladen."
#: members/templates/members/confirm_invitation.html
msgid ""
"Do you want to take part in the trial group meeting? If yes, please confirm "
"your attendance by clicking on the following button."
msgstr ""
"Möchtest du an der Schnupperstunde teilnehmen? Falls ja, bitte bestätige "
"deine Teilnahme durch das Betätigen des folgenden Knopfes."
#: members/templates/members/confirm_invitation.html
msgid "Confirm trial group meeting"
msgstr "Teilnahme bestätigen"
#: members/templates/members/confirm_success.html
msgid "Invitation confirmed"
msgstr "Teilnahme bestätigt"
#: members/templates/members/confirm_success.html
#, python-format
msgid ""
"You successfully confirmed the invitation to the trial group meeting of the "
"group %(groupname)s."
msgstr ""
"Deine Teilnahme an der Schnupperstunde der Gruppe %(groupname)s wurde "
"erfolgreich bestätigt."
#: members/templates/members/confirm_success.html
msgid ""
"We have informed the group leaders about your confirmation. If for some "
"reason you can not make it,\n"
"please contact the group leaders at"
msgstr ""
"Wir haben die Jugendleiter*innen der Jugendgruppe über deine Bestätigung "
"informiert. Falls du doch nicht zur Schnupperstunde kommen kannst, "
"informiere bitte die Jugendleiter*innen unter"
#: members/templates/members/echo.html
#: members/templates/members/echo_failed.html
#: members/templates/members/echo_password.html
@ -1942,10 +2005,6 @@ msgstr ""
msgid "Reject invitation"
msgstr "Einladung ablehnen"
#: members/templates/members/reject_invalid.html members/tests.py
msgid "This invitation is invalid or expired."
msgstr "Diese Einladung ist ungültig oder abgelaufen."
#: members/templates/members/reject_invitation.html
#, python-format
msgid ""
@ -2240,14 +2299,6 @@ msgstr "Ungültige Notfallkontakte"
#~ msgid "Good conduct certificate presentation needed"
#~ msgstr "Vorlage Führungszeugnis notwendig"
#, python-format
#~ msgid ""
#~ "Do you want to reject the invitation to a trial group meeting of the\n"
#~ "group %(invitation.group.name)s?"
#~ msgstr ""
#~ "Möchtest du die Einladung zur Schnupperstunde bei der Gruppe "
#~ "%(invitation.group.name)s wirklich ablehnen?"
#~ msgid "Yes, reject invitation"
#~ msgstr "Ja, Einladung ablehnen."

@ -18,7 +18,8 @@ from utils import RestrictedFileField, normalize_name
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_leave_waitinglist_link
get_invitation_reject_link, get_invite_as_user_key, get_leave_waitinglist_link,\
get_invitation_confirm_link
from django.contrib.auth.models import User
from django.conf import settings
from django.core.validators import MinValueValidator
@ -109,6 +110,14 @@ class Group(models.Model):
# return if the group has all relevant time slot information filled
return self.weekday and self.start_time and self.end_time
def get_time_info(self):
if self.has_time_info():
return settings.GROUP_TIME_AVAILABLE_TEXT.format(weekday=WEEKDAYS[self.weekday][1],
start_time=self.start_time.strftime('%H:%M'),
end_time=self.end_time.strftime('%H:%M'))
else:
return ""
def get_invitation_text_template(self):
"""The text template used to invite waiters to this group. This contains
placeholders for the name of the waiter and personalized links."""
@ -117,9 +126,7 @@ class Group(models.Model):
else:
group_link = ''
if self.has_time_info():
group_time = settings.GROUP_TIME_AVAILABLE_TEXT.format(weekday=WEEKDAYS[self.weekday][1],
start_time=self.start_time.strftime('%H:%M'),
end_time=self.end_time.strftime('%H:%M'))
group_time = self.get_time_info()
else:
group_time = settings.GROUP_TIME_UNAVAILABLE_TEXT.format(contact_email=self.contact_email)
return settings.INVITE_TEXT.format(group_time=group_time,
@ -912,6 +919,21 @@ class InvitationToGroup(models.Model):
settings.DEFAULT_SENDING_MAIL,
recipient.email)
def send_confirm_notification_to(self, recipient):
send_mail(_('Group invitation confirmed by %(waiter)s') % {'waiter': self.waiter},
settings.GROUP_INVITATION_CONFIRMED_TEXT.format(name=recipient.prename,
waiter=self.waiter,
group=self.group),
settings.DEFAULT_SENDING_MAIL,
recipient.email)
def send_confirm_confirmation(self):
self.waiter.send_mail(_('Trial group meeting confirmed'),
settings.TRIAL_GROUP_MEETING_CONFIRMED_TEXT.format(name=self.waiter.prename,
group=self.group,
contact_email=self.group.contact_email,
timeinfo=self.group.get_time_info()))
def notify_left_waitinglist(self):
"""
Inform youth leaders of the group and the inviter that the waiter left the waitinglist,
@ -932,6 +954,18 @@ class InvitationToGroup(models.Model):
for jl in self.group.leiters.all():
self.send_reject_notification_to(jl)
def confirm(self):
"""Confirm this invitation. Informs the youth leaders of the group of the invitation."""
self.rejected = False
self.save()
# confirm the confirmation
self.send_confirm_confirmation()
# send notifications
if self.created_by:
self.send_confirm_notification_to(self.created_by)
for jl in self.group.leiters.all():
self.send_confirm_notification_to(jl)
class MemberWaitingList(Person):
"""A participant on the waiting list"""
@ -1063,7 +1097,8 @@ class MemberWaitingList(Person):
self.send_mail(_("Invitation to trial group meeting"),
text_template.format(name=self.prename,
link=get_registration_link(invitation.key),
invitation_reject_link=get_invitation_reject_link(invitation.key)),
invitation_reject_link=get_invitation_reject_link(invitation.key),
invitation_confirm_link=get_invitation_confirm_link(invitation.key)),
cc=group.contact_email.email)
def unregister(self):

@ -0,0 +1,15 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Confirm invitation" %}
{% endblock %}
{% block content %}
<h1>{% trans "Confirm invitation" %}</h1>
<p>{% trans "This invitation is invalid or expired." %}</p>
{% endblock %}

@ -0,0 +1,29 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Confirm trial group meeting invitation" %}
{% endblock %}
{% block content %}
<h1>{% trans "Confirm trial group meeting invitation" %}</h1>
<p>
{% blocktrans %}You were invited to a trial group meeting of the group {{ groupname }}.{% endblocktrans %}
{{ timeinfo }}
</p>
<p>
{% blocktrans %}Do you want to take part in the trial group meeting? If yes, please confirm your attendance by clicking on the following button.{% endblocktrans %}
</p>
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="key" value="{{invitation.key}}">
<input type="submit" name="confirm_invitation"
value="{% trans "Confirm trial group meeting" %}"/>
</form>
{% endblock %}

@ -0,0 +1,23 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Invitation confirmed" %}
{% endblock %}
{% block content %}
<h1>{% trans "Invitation confirmed" %}</h1>
<p>
{% blocktrans %}You successfully confirmed the invitation to the trial group meeting of the group {{ groupname }}.{% endblocktrans %}
{{ timeinfo }}
</p>
<p>
{% blocktrans %}We have informed the group leaders about your confirmation. If for some reason you can not make it,
please contact the group leaders at{% endblocktrans %}
<a href="mailto:{{ contact_email }}">{{ contact_email }}</a>.
</p>
{% endblock %}

@ -12,6 +12,7 @@ urlpatterns = [
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/confirm', views.confirm_invitation , name='confirm_invitation'),
re_path(r'^waitinglist', views.register_waiting_list , name='register_waiting_list'),
re_path(r'^mail/confirm', views.confirm_mail , name='confirm_mail'),
]

@ -479,6 +479,47 @@ def reject_invitation(request):
return render_reject_invalid(request)
def render_confirm_invitation(request, invitation):
return render(request, 'members/confirm_invitation.html',
{'invitation': invitation,
'groupname': invitation.group.name,
'contact_email': invitation.group.contact_email,
'timeinfo': invitation.group.get_time_info()})
def render_confirm_invalid(request):
return render(request, 'members/confirm_invalid.html')
def render_confirm_success(request, invitation):
return render(request, 'members/confirm_success.html',
{'invitation': invitation,
'groupname': invitation.group.name,
'contact_email': invitation.group.contact_email,
'timeinfo': invitation.group.get_time_info()})
def confirm_invitation(request):
if request.method == 'GET' and 'key' in request.GET:
key = request.GET['key']
try:
invitation = InvitationToGroup.objects.get(key=key)
if invitation.rejected or invitation.is_expired():
raise ValueError
return render_confirm_invitation(request, invitation)
except (ValueError, InvitationToGroup.DoesNotExist):
return render_confirm_invalid(request)
if request.method != 'POST' or 'key' not in request.POST:
return render_confirm_invalid(request)
key = request.POST['key']
try:
invitation = InvitationToGroup.objects.get(key=key)
except InvitationToGroup.DoesNotExist:
return render_confirm_invalid(request)
invitation.confirm()
return render_confirm_success(request, invitation)
def confirm_waiting(request):
if request.method == 'GET' and 'key' in request.GET:
key = request.GET['key']

Loading…
Cancel
Save