From 31b31a3eb34becb5fa0576ba85c6a307574f0f70 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Fri, 27 Dec 2024 18:30:04 +0100 Subject: [PATCH] members/waitinglist: allow editing of invite text --- .../jdav_web/settings/components/texts.py | 6 +- jdav_web/members/admin.py | 102 ++-- .../members/locale/de/LC_MESSAGES/django.po | 463 ++++++++++-------- jdav_web/members/models.py | 48 +- .../templates/admin/invite_for_group.html | 14 +- .../admin/invite_for_group_text.html | 68 +++ 6 files changed, 427 insertions(+), 274 deletions(-) create mode 100644 jdav_web/members/templates/admin/invite_for_group_text.html diff --git a/jdav_web/jdav_web/settings/components/texts.py b/jdav_web/jdav_web/settings/components/texts.py index d1e5eaf..b13914c 100644 --- a/jdav_web/jdav_web/settings/components/texts.py +++ b/jdav_web/jdav_web/settings/components/texts.py @@ -26,7 +26,7 @@ GROUP_TIME_AVAILABLE_TEXT = """Die Gruppenstunde findet jeden {weekday} von {sta GROUP_TIME_UNAVAILABLE_TEXT = """Bitte erfrage die Gruppenzeiten bei der Gruppenleitung ({contact_email}).""" -INVITE_TEXT = """Hallo {name}, +INVITE_TEXT = """Hallo {{name}}, wir haben gute Neuigkeiten für dich. Es ist ein Platz in der Jugendgruppe {group_name} {group_link}freigeworden. {group_time} @@ -38,7 +38,7 @@ Informationen und deine Anmeldebestätigung von dir. Die lädst du herunter (siehe %(REGISTRATION_FORM_DOWNLOAD_LINK)s ), lässt sie von deinen Eltern ausfüllen, unterschreiben und lädst ein Foto davon in unserem Anmeldeformular hoch. Das kannst du alles über folgenden Link erledigen: -{link} +{{link}} Du siehst dort auch die Daten, die du bei deiner Eintragung auf die Warteliste angegeben hast. Bitte überprüfe, ob die Daten noch stimmen und ändere sie bei Bedarf ab. @@ -46,7 +46,7 @@ Du siehst dort auch die Daten, die du bei deiner Eintragung auf die Warteliste a Falls du zu dem obigen Termin keine Zeit hast oder dich ganz von der Warteliste abmelden möchtest, lehne bitte diese Einladung unter folgendem Link ab: -{invitation_reject_link} +{{invitation_reject_link}} Bei Fragen, wende dich gerne an %(RESPONSIBLE_MAIL)s. diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index cc4c184..cc0b56a 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -561,6 +561,12 @@ class WaiterInviteForm(forms.Form): label=_('Group')) +class WaiterInviteTextForm(forms.Form): + _selected_action = forms.CharField(widget=forms.MultipleHiddenInput) + text_template = forms.CharField(label=_('Invitation text'), + widget=forms.Textarea(attrs={'rows': 30, 'cols': 100})) + + class InvitationToGroupAdmin(admin.TabularInline): model = InvitationToGroup fields = ['group', 'date', 'status'] @@ -594,7 +600,7 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): 'confirmed_mail', 'waiting_confirmed', 'sent_reminders') search_fields = ('prename', 'lastname', 'email') list_filter = ['confirmed_mail', 'gender', InvitedToGroupFilter] - actions = ['ask_for_registration', 'ask_for_wait_confirmation'] + actions = ['ask_for_registration_action', 'ask_for_wait_confirmation'] inlines = [InvitationToGroupAdmin] readonly_fields= ['application_date', 'sent_reminders'] @@ -609,38 +615,6 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): _("Successfully asked %(name)s to confirm their waiting status.") % {'name': waiter.name}) ask_for_wait_confirmation.short_description = _('Ask selected waiters to confirm their waiting status') - def ask_for_registration(self, request, queryset): - """Asks the waiting person to register with all required data.""" - if "apply" in request.POST: - try: - group = Group.objects.get(pk=request.POST['group']) - except Group.DoesNotExist: - messages.error(request, - _("An error occurred while trying to invite said members. Please try again.")) - return HttpResponseRedirect(request.get_full_path()) - if not group.contact_email: - messages.error(request, - _('The selected group does not have a contact email. Please first set a contact email and then try again.')) - return HttpResponseRedirect(request.get_full_path()) - - for waiter in queryset: - waiter.invited_for_group = group - waiter.save() - waiter.invite_to_group(group) - messages.success(request, - _("Successfully invited %(name)s to %(group)s.") % {'name': waiter.name, 'group': waiter.invited_for_group.name}) - - return HttpResponseRedirect(request.get_full_path()) - context = dict(self.admin_site.each_context(request), - title=_('Select group for invitation'), - opts=self.opts, - waiters=queryset.all(), - form=WaiterInviteForm(initial={'_selected_action': queryset.values_list('id', flat=True)})) - return render(request, - 'admin/invite_selected_for_group.html', - context=context) - ask_for_registration.short_description = _('Offer waiter a place in a group.') - def response_change(self, request, waiter): ret = super(MemberWaitingListAdmin, self).response_change(request, waiter) if "_invite" in request.POST: @@ -672,8 +646,19 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): queryset = super().get_queryset(request) return queryset.prefetch_related('invitationtogroup_set') + def ask_for_registration_action(self, request, queryset): + return self.invite_view(request, queryset) + ask_for_registration_action.short_description = _('Offer waiter a place in a group.') + def invite_view(self, request, object_id): - waiter = MemberWaitingList.objects.get(pk=object_id) + if type(object_id) == str: + waiter = MemberWaitingList.objects.get(pk=object_id) + queryset = [waiter] + id_list = [waiter.pk] + else: + waiter = None + queryset = object_id + id_list = queryset.values_list('id', flat=True) if "apply" in request.POST: try: @@ -687,22 +672,49 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): messages.error(request, _('The selected group does not have a contact email. Please first set a contact email and then try again.')) return HttpResponseRedirect(request.get_full_path()) + context = dict(self.admin_site.each_context(request), + title=_('Select group for invitation'), + opts=self.opts, + group=group, + queryset=queryset, + form=WaiterInviteTextForm(initial={ + '_selected_action': id_list, + 'text_template': group.get_invitation_text_template() + })) + if waiter: + context = dict(context, object=waiter, waiter=waiter) + return render(request, + 'admin/invite_for_group_text.html', + context=context) + + if "send" in request.POST: + try: + group = Group.objects.get(pk=request.POST['group']) + text_template = request.POST['text_template'] + except (Group.DoesNotExist, KeyError): + messages.error(request, + _("An error occurred while trying to invite said members. Please try again.")) + return HttpResponseRedirect(request.get_full_path()) + for w in queryset: + w.invite_to_group(group, text_template=text_template) + messages.success(request, + _("Successfully invited %(name)s to %(group)s.") % {'name': w.name, 'group': w.invited_for_group.name}) - waiter.invited_for_group = group - waiter.save() - waiter.invite_to_group(group) - messages.success(request, - _("Successfully invited %(name)s to %(group)s.") % {'name': waiter.name, 'group': waiter.invited_for_group.name}) - - return HttpResponseRedirect(reverse('admin:%s_%s_change' % (waiter._meta.app_label, waiter._meta.model_name), - args=(object_id,))) + if waiter: + return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), + args=(object_id,))) + else: + return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name))) context = dict(self.admin_site.each_context(request), title=_('Select group for invitation'), opts=self.opts, - object=waiter, - waiter=waiter, - form=WaiterInviteForm(initial={'_selected_action': [waiter.pk]})) + queryset=queryset, + form=WaiterInviteForm(initial={ + '_selected_action': id_list + })) + if waiter: + context = dict(context, object=waiter, waiter=waiter) return render(request, 'admin/invite_for_group.html', context=context) diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po index cfd1dbb..129fad9 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: 2024-12-19 01:21+0100\n" +"POT-Creation-Date: 2024-12-27 18:18+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: members/admin.py:127 members/models.py:412 +#: members/admin.py:127 members/models.py:430 msgid "Registration complete" msgstr "Anmeldung vollständig" @@ -34,174 +34,182 @@ msgstr "Nein" msgid "All" msgstr "Alle" -#: members/admin.py:185 members/admin.py:415 +#: members/admin.py:185 members/admin.py:414 msgid "Contact information" msgstr "Kontaktinformationen" -#: members/admin.py:190 members/admin.py:420 +#: members/admin.py:190 members/admin.py:419 msgid "Skills" msgstr "Fähigkeiten" -#: members/admin.py:195 members/admin.py:425 +#: members/admin.py:195 members/admin.py:424 msgid "Others" msgstr "Sonstiges" -#: members/admin.py:201 members/admin.py:430 +#: members/admin.py:201 members/admin.py:429 msgid "Organizational" msgstr "Organisatorisches" -#: members/admin.py:283 +#: members/admin.py:282 msgid "Compose new mail to selected members" msgstr "Neue Nachricht an ausgewählte Teilnehmer*innen verfassen" -#: members/admin.py:289 +#: members/admin.py:288 msgid "Echo required" msgstr "Rückmeldung erforderlich" -#: members/admin.py:291 +#: members/admin.py:290 msgid "Successfully requested echo from selected members." msgstr "" "Rückmeldungsaufforderung erfolgreich an ausgewählte Teilnehmer*innen " "verschickt." -#: members/admin.py:292 +#: members/admin.py:291 msgid "Request echo from selected members" msgstr "Rückmeldungsaufforderung an ausgewählte Teilnehmer*innen verschicken" -#: members/admin.py:301 +#: members/admin.py:300 #, python-format msgid "%(name)s does not have a DAV360 email address or is already registered." msgstr "%(name)s hat keine DAV360 E-Mail Adresse oder ist bereits registriert." -#: members/admin.py:303 +#: members/admin.py:302 #, python-format msgid "Successfully invited %(name)s as user." msgstr "Erfolgreich %(name)s aufgefordert Zugangsdaten zu wählen." -#: members/admin.py:305 +#: members/admin.py:304 msgid "Successfully invited selected members to join as users." msgstr "" "Erfolgreich ausgewählte Teilnehmer*innen aufgefordert Zugangsdaten zu wählen." -#: members/admin.py:307 +#: members/admin.py:306 msgid "Some members have been invited, others could not be invited." msgstr "" "Manche Teilnehmer*innen wurden eingeladen, andere konnten nicht eingeladen " "werden." -#: members/admin.py:314 members/admin.py:331 +#: members/admin.py:313 members/admin.py:330 msgid "Permission denied." msgstr "Fehlende Berechtigungen." -#: members/admin.py:321 members/admin.py:355 +#: members/admin.py:320 members/admin.py:354 #: members/templates/admin/invite_as_user.html:21 msgid "Invite as user" msgstr "Kompass Zugangsdaten wählen lassen" -#: members/admin.py:326 +#: members/admin.py:325 msgid "Invite selected members to join Kompass as users." msgstr "Ausgewählte Teilnehmer*innen Kompass Zugangsdaten wählen lassen." -#: members/admin.py:337 +#: members/admin.py:336 msgid "Member not found." msgstr "Teilnehmer*in nicht gefunden." -#: members/admin.py:341 +#: members/admin.py:340 #, python-format msgid "%(name)s already has login data." msgstr "%(name)s hat schon Zugangsdaten." -#: members/admin.py:346 +#: members/admin.py:345 #, python-format msgid "The configured email address for %(name)s is not an internal one." msgstr "Die für %(name)s eingestellte E-Mail Adresse ist keine DAV360 Adresse." -#: members/admin.py:360 +#: members/admin.py:359 #, python-format msgid "%(name)s already has a pending invitation as user." msgstr "" "%(name)s hat bereits eine ausstehende Aufforderung Zugangsdaten zu wählen." -#: members/admin.py:378 +#: members/admin.py:377 msgid "activity" msgstr "Aktivität" -#: members/admin.py:388 members/models.py:56 members/models.py:1572 +#: members/admin.py:387 members/models.py:56 members/models.py:1584 msgid "Name" msgstr "Name" -#: members/admin.py:479 +#: members/admin.py:478 msgid "Successfully requested mail confirmation from selected registrations." msgstr "Aufforderung zur Bestätigung der Email Adresse versendet." -#: members/admin.py:480 +#: members/admin.py:479 msgid "Request mail confirmation from selected registrations" msgstr "Aufforderung zur Bestätigung der Email Adresse versenden" -#: members/admin.py:487 members/admin.py:552 +#: members/admin.py:486 members/admin.py:551 #, python-format msgid "Successfully confirmed %(name)s." msgstr "Registrierung von %(name)s erfolgreich bestätigt." -#: members/admin.py:491 members/admin.py:555 +#: members/admin.py:490 members/admin.py:554 #, python-format msgid "Can't confirm. %(name)s has unconfirmed email addresses." msgstr "Bestätigung nicht möglich. %(name)s hat unbestätigte Emailadressen." -#: members/admin.py:496 +#: members/admin.py:495 msgid "Successfully confirmed multiple registrations." msgstr "Erfolgreich mehrere Registrierungen bestätigt." -#: members/admin.py:498 +#: members/admin.py:497 msgid "" "Failed to confirm some registrations because of unconfirmed email addresses." msgstr "" "Einige Bestätigungen fehlgeschlagen, weil Emailadressen noch nicht bestätigt " "sind." -#: members/admin.py:499 +#: members/admin.py:498 msgid "Confirm selected registrations" msgstr "Ausgewählte Registrierungen bestätigen" -#: members/admin.py:522 +#: members/admin.py:521 msgid "Demote selected registrations to waiters." msgstr "Ausgewählte Registrierungen zurück auf die Warteliste setzen." -#: members/admin.py:538 +#: members/admin.py:537 msgid "Demote member to waiter" msgstr "Ausgewählte Registrierung zurück auf die Warteliste setzen." -#: members/admin.py:547 +#: members/admin.py:546 #, python-format msgid "Successfully demoted %(name)s to waiter." msgstr "%(name)s zurück auf die Warteliste gesetzt." -#: members/admin.py:562 members/models.py:419 members/models.py:822 -#: members/models.py:1317 +#: members/admin.py:561 members/models.py:437 members/models.py:840 +#: members/models.py:1329 msgid "Group" msgstr "Gruppe" -#: members/admin.py:577 +#: members/admin.py:566 +msgid "Invitation text" +msgstr "Einladungstext" + +#: members/admin.py:582 msgid "Pending group invitation for group" msgstr "Ausstehende Gruppeneinladung für Gruppe" -#: members/admin.py:610 +#: members/admin.py:615 #, python-format msgid "Successfully asked %(name)s to confirm their waiting status." msgstr "Erfolgreich %(name)s aufgefordert den Wartelistenplatz zu bestätigen." -#: members/admin.py:611 +#: members/admin.py:616 msgid "Ask selected waiters to confirm their waiting status" msgstr "Wartende auffordern den Wartelistenplatz zu bestätigen" -#: members/admin.py:620 members/admin.py:684 +#: members/admin.py:651 members/admin.py:681 +msgid "Offer waiter a place in a group." +msgstr "Personen auf der Warteliste einen Gruppenplatz anbieten." + +#: members/admin.py:660 members/admin.py:698 members/admin.py:726 msgid "" "An error occurred while trying to invite said members. Please try again." msgstr "" "Beim Einladen dieser Personen ist ein Fehler aufgetreten. Bitte versuche es " "nochmal. " -#: members/admin.py:624 members/admin.py:689 +#: members/admin.py:664 members/admin.py:703 msgid "" "The selected group does not have a contact email. Please first set a contact " "email and then try again." @@ -209,43 +217,39 @@ msgstr "" "Die ausgewählte Gruppe hat keine Kontakt E-Mail Adresse. Bitte stelle eine " "Kontakt E-Mail Adresse ein und versuche es erneut." -#: members/admin.py:632 members/admin.py:696 +#: members/admin.py:670 members/admin.py:731 #, python-format msgid "Successfully invited %(name)s to %(group)s." msgstr "Erfolgreich %(name)s zu Gruppe %(group)s eingeladen." -#: members/admin.py:636 members/admin.py:702 +#: members/admin.py:674 members/admin.py:706 members/admin.py:740 msgid "Select group for invitation" msgstr "Wähle Gruppe für Einladung aus" -#: members/admin.py:643 -msgid "Offer waiter a place in a group." -msgstr "Personen auf der Warteliste einen Gruppenplatz anbieten." - -#: members/admin.py:719 members/models.py:72 +#: members/admin.py:760 members/models.py:72 msgid "name" msgstr "Name" -#: members/admin.py:720 +#: members/admin.py:761 msgid "" "The group name may only consist of letters, numerals, _, -, :, * and spaces." msgstr "" "Der Gruppenname darf nur aus Buchstaben, Zahlen, _, -, :, * oder Leerzeichen " "bestehen." -#: members/admin.py:749 +#: members/admin.py:790 msgid "Difficulty" msgstr "Schwierigkeit" -#: members/admin.py:752 +#: members/admin.py:793 msgid "Tour type" msgstr "Art der Tour" -#: members/admin.py:755 members/models.py:1048 +#: members/admin.py:796 members/models.py:1060 msgid "Means of transportation" msgstr "Verkehrsmittel" -#: members/admin.py:781 +#: members/admin.py:823 msgid "" "Please list here all expenses in relation with this excursion and upload " "relevant bills. These have to be permanently stored for the application of " @@ -258,7 +262,7 @@ msgstr "" "einzelnen Posten wird dabei auf der LJP-Kostenübersicht angezeigt (sinnvoll " "wären z.B. Anreise, Verpflegung, Material etc.)." -#: members/admin.py:799 +#: members/admin.py:841 msgid "" "Here you can work on a seminar report for applying for financial " "contributions from Landesjugendplan (LJP). More information on creating a " @@ -271,7 +275,7 @@ msgstr "" "wahlweise nur TN-Liste und Kostenübersicht kannst du anschließend " "herunterladen." -#: members/admin.py:807 +#: members/admin.py:849 msgid "" "Please list all participants (also youth leaders) of this excursion. Here " "you can still make changes just before departure and hence generate the " @@ -282,34 +286,34 @@ msgstr "" "jederzeit die aktuelle Teilnehmer*innenliste für die Krisenintervention " "generieren." -#: members/admin.py:853 +#: members/admin.py:895 #, python-format msgid "You are not allowed to view all members on note list %(name)s." msgstr "" "Du hast nicht die nötigen Rechte um alle Teilnehmer*innen der Notizliste " "%(name)s anzusehen." -#: members/admin.py:863 +#: members/admin.py:905 msgid "Generate PDF summary" msgstr "Übersicht erstellen" -#: members/admin.py:867 +#: members/admin.py:909 msgid "Full report" msgstr "Vollständiger Seminarbericht" -#: members/admin.py:868 +#: members/admin.py:910 msgid "Costs and participants only" msgstr "Nur Kosten und Teilnehmende" -#: members/admin.py:869 +#: members/admin.py:911 msgid "Mode" msgstr "Modus" -#: members/admin.py:870 +#: members/admin.py:912 msgid "Prepend V32" msgstr "V32 Formblatt einfügen" -#: members/admin.py:886 +#: members/admin.py:928 msgid "" "General information on your excursion. These are partly relevant for the " "amount of financial compensation (means of transport, travel distance, etc.)." @@ -318,48 +322,48 @@ msgstr "" "teilweise relevant für die Zuschüsse aus dem Jugendetat (Verkehrsmittel, " "Fahrstrecke in km)." -#: members/admin.py:916 +#: members/admin.py:958 #, python-format msgid "You are not allowed to view all members on excursion %(name)s." msgstr "" "Du hast nicht die nötigen Rechte um alle Teilnehmer*innen der Ausfahrt " "%(name)s anzusehen." -#: members/admin.py:924 +#: members/admin.py:966 msgid "Generate crisis intervention list" msgstr "Kriseninterventionsliste erstellen" -#: members/admin.py:932 +#: members/admin.py:974 msgid "Generate overview" msgstr "Hinweise für Jugendleiter erstellen" -#: members/admin.py:936 members/admin.py:968 +#: members/admin.py:978 members/admin.py:1010 #: members/templates/admin/generate_seminar_report.html:21 msgid "Generate seminar report" msgstr "Landesjugendplan Antrag erstellen" -#: members/admin.py:949 +#: members/admin.py:991 msgid "Please select a mode." msgstr "Bitte wähle einen Modus aus." -#: members/admin.py:954 +#: members/admin.py:996 msgid "" "Full mode is only available, if the seminar report section is filled out." msgstr "" "Der vollständiger Modus ist nur verfügbar, wenn der Seminarbericht " "ausgefüllt ist. " -#: members/admin.py:980 +#: members/admin.py:1022 msgid "Generate SJR application" msgstr "SJR Antrag erstellen" -#: members/admin.py:984 +#: members/admin.py:1026 msgid "No statement found. Please add a statement and then retry." msgstr "" "Keine Abrechnung angelegt. Bitte lege eine Abrechnung and und versuche es " "erneut." -#: members/admin.py:988 +#: members/admin.py:1030 msgid "" "Successfully submited statement. The finance department will notify you as " "soon as possible." @@ -367,7 +371,7 @@ msgstr "" "Abrechnung erfolgreich eingericht. Die Finanzabteilung wird sich bei dir so " "schnell wie möglich melden." -#: members/admin.py:991 +#: members/admin.py:1033 #: members/templates/admin/freizeit_finance_overview.html:21 msgid "Finance overview" msgstr "Kostenübersicht" @@ -404,11 +408,11 @@ msgstr "Samstag" msgid "Sunday" msgstr "Sonntag" -#: members/models.py:57 members/models.py:1034 +#: members/models.py:57 members/models.py:1046 msgid "Description" msgstr "Beschreibung" -#: members/models.py:63 members/models.py:1026 +#: members/models.py:63 members/models.py:1038 #: members/templates/members/change_member.html:18 msgid "Activity" msgstr "Aktivität" @@ -441,7 +445,7 @@ msgstr "Jugendleiter" msgid "week day" msgstr "Wochentag" -#: members/models.py:80 members/models.py:1399 +#: members/models.py:80 members/models.py:1411 msgid "Starting time" msgstr "Zeitpunkt" @@ -453,7 +457,7 @@ msgstr "Endzeitpunkt" msgid "Contact email" msgstr "Kontakt Email" -#: members/models.py:93 members/models.py:276 +#: members/models.py:93 members/models.py:294 msgid "group" msgstr "Gruppe" @@ -461,159 +465,159 @@ msgstr "Gruppe" msgid "groups" msgstr "Gruppen" -#: members/models.py:110 +#: members/models.py:128 msgid "prename" msgstr "Vorname" -#: members/models.py:111 +#: members/models.py:129 msgid "last name" msgstr "Nachname" -#: members/models.py:114 +#: members/models.py:132 msgid "Email confirmed" msgstr "Emailadresse bestätigt" -#: members/models.py:132 members/models.py:203 members/models.py:250 +#: members/models.py:150 members/models.py:221 members/models.py:268 msgid "phone number" msgstr "Telefonnummer (mobil)" -#: members/models.py:163 +#: members/models.py:181 msgid "Email confirmation needed" msgstr "Email Bestätigung erforderlich" -#: members/models.py:213 +#: members/models.py:231 msgid "birth date" msgstr "Geburtsdatum" -#: members/models.py:218 +#: members/models.py:236 msgid "Gender" msgstr "Gender" -#: members/models.py:219 +#: members/models.py:237 msgid "comments" msgstr "Kommentare" -#: members/models.py:247 +#: members/models.py:265 msgid "Alternative email confirmed" msgstr "Alternative E-Mail Adresse bestätigt" -#: members/models.py:251 +#: members/models.py:269 msgid "street and house number" msgstr "Straße und Hausnummer" -#: members/models.py:252 +#: members/models.py:270 msgid "Postcode" msgstr "PLZ" -#: members/models.py:254 +#: members/models.py:272 msgid "town" msgstr "Stadt" -#: members/models.py:255 +#: members/models.py:273 msgid "Address extra" msgstr "Adress-Zusatz" -#: members/models.py:256 +#: members/models.py:274 msgid "Country" msgstr "Land" -#: members/models.py:258 +#: members/models.py:276 msgid "Good conduct certificate presented on" msgstr "Führungszeugnis vorgelegt am" -#: members/models.py:259 +#: members/models.py:277 msgid "Joined on" msgstr "Eintritt" -#: members/models.py:260 +#: members/models.py:278 msgid "Left on" msgstr "Austritt" -#: members/models.py:261 +#: members/models.py:279 msgid "Has key" msgstr "Hat Jugendraumschlüssel" -#: members/models.py:262 +#: members/models.py:280 msgid "Has a free ticket for the climbing gym" msgstr "Hat Freikarte für Kletterhalle" -#: members/models.py:263 +#: members/models.py:281 msgid "DAV badge number" msgstr "DAV Mitgliedsnummer" -#: members/models.py:264 +#: members/models.py:282 msgid "Knows how to swim" msgstr "Kann schwimmen" -#: members/models.py:265 +#: members/models.py:283 msgid "Climbing badge" msgstr "Kletterschein" -#: members/models.py:266 +#: members/models.py:284 msgid "Alpine experience" msgstr "Alpine Erfahrung" -#: members/models.py:267 +#: members/models.py:285 msgid "Allergies" msgstr "Allergieen" -#: members/models.py:268 +#: members/models.py:286 msgid "Medication" msgstr "Medikamente" -#: members/models.py:269 +#: members/models.py:287 msgid "Tetanus vaccination" msgstr "Tetanusimpfung" -#: members/models.py:270 +#: members/models.py:288 msgid "Photos may be taken" msgstr "Fotoerlaubnis" -#: members/models.py:271 +#: members/models.py:289 msgid "Legal guardians" msgstr "Erziehungsberechtigte" -#: members/models.py:273 +#: members/models.py:291 msgid "May cancel a group appointment independently" msgstr "Darf sich allein von der Gruppenstunde abmelden" -#: members/models.py:280 +#: members/models.py:298 msgid "receives newsletter" msgstr "Erhält den Newsletter" -#: members/models.py:284 +#: members/models.py:302 msgid "created" msgstr "erstellt" -#: members/models.py:285 +#: members/models.py:303 msgid "Active" msgstr "Aktiv" -#: members/models.py:286 +#: members/models.py:304 msgid "registration form" msgstr "Anmeldeformular" -#: members/models.py:295 +#: members/models.py:313 msgid "image" msgstr "Bild" -#: members/models.py:304 +#: members/models.py:322 msgid "Echoed" msgstr "Rückgemeldet" -#: members/models.py:305 +#: members/models.py:323 msgid "Confirmed" msgstr "Bestätigt" -#: members/models.py:307 +#: members/models.py:325 msgid "Login data" msgstr "Zugangsdaten" -#: members/models.py:309 +#: members/models.py:327 msgid "waitinglist application date" msgstr "Wartelistenbewerbungsdatum" -#: members/models.py:311 +#: members/models.py:329 msgid "" "If the person registered from the waitinglist, this is their application " "date." @@ -621,351 +625,351 @@ msgstr "" "Falls sich die Person über die Warteliste angemeldet hat ist dies ihr " "Bewerbungsdatum." -#: members/models.py:340 +#: members/models.py:358 msgid "Good conduct certificate valid" msgstr "Führungszeugnis gültig" -#: members/models.py:422 +#: members/models.py:440 msgid "member" msgstr "Teilnehmer*in" -#: members/models.py:423 +#: members/models.py:441 msgid "members" msgstr "Teilnehmer*innen" -#: members/models.py:501 +#: members/models.py:519 msgid "Upload registration form" msgstr "Anmeldeformular hochladen" -#: members/models.py:512 +#: members/models.py:530 #, python-format msgid "New unconfirmed registration for group %(group)s" msgstr "Neue unbestätigte Registrierung für Gruppe %(group)s" -#: members/models.py:738 +#: members/models.py:756 msgid "Set login data for Kompass" msgstr "Zugangsdaten für Kompass wählen" -#: members/models.py:773 members/models.py:982 members/models.py:993 -#: members/models.py:1348 members/models.py:1355 +#: members/models.py:791 members/models.py:994 members/models.py:1005 +#: members/models.py:1360 members/models.py:1367 msgid "Member" msgstr "Teilnehmer*in" -#: members/models.py:780 +#: members/models.py:798 msgid "Emergency contact" msgstr "Notfallkontakt" -#: members/models.py:781 +#: members/models.py:799 msgid "Emergency contacts" msgstr "Notfallkontakte" -#: members/models.py:801 +#: members/models.py:819 msgid "Unconfirmed registration" msgstr "Unbestätigte Registrierung" -#: members/models.py:802 +#: members/models.py:820 msgid "Unconfirmed registrations" msgstr "Unbestätigte Registrierungen" -#: members/models.py:821 members/models.py:866 +#: members/models.py:839 members/models.py:884 msgid "Waiter" msgstr "Wartende Person" -#: members/models.py:823 +#: members/models.py:841 msgid "Invitation date" msgstr "Einladungsdatum" -#: members/models.py:824 members/templates/members/reject_success.html:6 +#: members/models.py:842 members/templates/members/reject_success.html:6 #: members/templates/members/reject_success.html:11 msgid "Invitation rejected" msgstr "Einladung abgelehnt" -#: members/models.py:828 +#: members/models.py:846 msgid "Invitation to group" msgstr "Gruppeneinladung" -#: members/models.py:829 +#: members/models.py:847 msgid "Invitations to groups" msgstr "Gruppeneinladungen" -#: members/models.py:836 +#: members/models.py:854 msgid "Rejected" msgstr "Abgelehnt" -#: members/models.py:838 +#: members/models.py:856 msgid "Expired" msgstr "Abgelaufen" -#: members/models.py:840 +#: members/models.py:858 msgid "Undecided" msgstr "Ausstehend" -#: members/models.py:841 +#: members/models.py:859 msgid "Status" msgstr "Status" -#: members/models.py:852 +#: members/models.py:870 msgid "Do you want to tell us something else?" msgstr "Möchtest du uns noch etwas mitteilen?" -#: members/models.py:853 +#: members/models.py:871 msgid "application date" msgstr "Bewerbungsdatum" -#: members/models.py:855 +#: members/models.py:873 msgid "Last wait confirmation" msgstr "Letzte Wartebestätigung" -#: members/models.py:859 +#: members/models.py:877 msgid "Last reminder" msgstr "Letzte Erinnerung" -#: members/models.py:860 +#: members/models.py:878 msgid "Missed reminders" msgstr "Verpasste Erinnerungen" -#: members/models.py:867 +#: members/models.py:885 msgid "Waiters" msgstr "Warteliste" -#: members/models.py:882 +#: members/models.py:900 msgid "Latest group invitation" msgstr "Letzte Gruppeneinladung" -#: members/models.py:899 +#: members/models.py:917 msgid "Waiting status confirmed" msgstr "Wartelistenplatz bestätigt" -#: members/models.py:906 +#: members/models.py:924 msgid "Waiting confirmation needed" msgstr "Wartelistenplatzbestätigung erforderlich" -#: members/models.py:961 +#: members/models.py:977 msgid "Invitation to trial group meeting" msgstr "Einladung zu Schnupperstunde" -#: members/models.py:973 +#: members/models.py:985 msgid "Unregistered from waiting list" msgstr "Von der Warteliste abgemeldet" -#: members/models.py:987 +#: members/models.py:999 msgid "Comment" msgstr "Kommentar" -#: members/models.py:994 members/models.py:1356 +#: members/models.py:1006 members/models.py:1368 msgid "Members" msgstr "Teilnehmer*innen" -#: members/models.py:1028 +#: members/models.py:1040 msgid "Place" msgstr "Stützpunkt / Ort" -#: members/models.py:1029 +#: members/models.py:1041 msgid "Destination (optional)" msgstr "ggf. Ziel" -#: members/models.py:1031 +#: members/models.py:1043 msgid "e.g. a peak" msgstr "z.B. ein Gipfel" -#: members/models.py:1032 +#: members/models.py:1044 msgid "Begin" msgstr "Anfang" -#: members/models.py:1033 +#: members/models.py:1045 msgid "End (optional)" msgstr "Ende" -#: members/models.py:1036 +#: members/models.py:1048 msgid "Groups" msgstr "Gruppen" -#: members/models.py:1049 +#: members/models.py:1061 msgid "Kilometers traveled" msgstr "Fahrstrecke in Kilometer" -#: members/models.py:1052 +#: members/models.py:1064 msgid "Categories" msgstr "Kategorien" -#: members/models.py:1053 +#: members/models.py:1065 msgid "easy" msgstr "leicht" -#: members/models.py:1053 +#: members/models.py:1065 msgid "medium" msgstr "mittel" -#: members/models.py:1053 +#: members/models.py:1065 msgid "hard" msgstr "schwer" -#: members/models.py:1063 members/models.py:1379 +#: members/models.py:1075 members/models.py:1391 #: members/templates/admin/freizeit_finance_overview.html:26 msgid "Excursion" msgstr "Ausfahrt" -#: members/models.py:1064 +#: members/models.py:1076 msgid "Excursions" msgstr "Ausfahrten" -#: members/models.py:1294 members/models.py:1370 members/models.py:1586 +#: members/models.py:1306 members/models.py:1382 members/models.py:1598 msgid "Title" msgstr "Titel" -#: members/models.py:1295 members/models.py:1313 members/models.py:1587 +#: members/models.py:1307 members/models.py:1325 members/models.py:1599 msgid "Date" msgstr "Datum" -#: members/models.py:1314 +#: members/models.py:1326 msgid "Location" msgstr "Ort" -#: members/models.py:1315 +#: members/models.py:1327 msgid "Topic" msgstr "Thema" -#: members/models.py:1339 +#: members/models.py:1351 msgid "Jugendleiter" msgstr "Jugendleiter" -#: members/models.py:1342 +#: members/models.py:1354 msgid "Klettertreff" msgstr "Klettertreff" -#: members/models.py:1343 +#: members/models.py:1355 msgid "Klettertreffs" msgstr "Klettertreffs" -#: members/models.py:1361 +#: members/models.py:1373 msgid "Password" msgstr "Passwort" -#: members/models.py:1364 +#: members/models.py:1376 msgid "registration password" msgstr "Registrierungspassort" -#: members/models.py:1365 +#: members/models.py:1377 msgid "registration passwords" msgstr "Registrierungspasswörter" -#: members/models.py:1372 +#: members/models.py:1384 msgid "Alpinistic goals" msgstr "Alpintechnische Ziele" -#: members/models.py:1373 +#: members/models.py:1385 msgid "Pedagogic goals" msgstr "Pädagogische Ziele" -#: members/models.py:1374 +#: members/models.py:1386 msgid "Content and methods" msgstr "Inhalte und Methoden" -#: members/models.py:1375 +#: members/models.py:1387 msgid "Evaluation" msgstr "Wertung" -#: members/models.py:1376 +#: members/models.py:1388 msgid "Experiences and possible improvements" msgstr "Erfahrungen und Verbesserungsvorschläge" -#: members/models.py:1385 members/models.py:1406 +#: members/models.py:1397 members/models.py:1418 msgid "LJP Proposal" msgstr "Seminarbericht" -#: members/models.py:1386 +#: members/models.py:1398 msgid "LJP Proposals" msgstr "Seminarberichte" -#: members/models.py:1400 +#: members/models.py:1412 msgid "Duration in hours" msgstr "Dauer in Stunden" -#: members/models.py:1403 +#: members/models.py:1415 msgid "Activity and method" msgstr "Art der Aktion inkl. Methode" -#: members/models.py:1411 +#: members/models.py:1423 msgid "Intervention" msgstr "Aktion" -#: members/models.py:1412 +#: members/models.py:1424 msgid "Interventions" msgstr "Aktionen" -#: members/models.py:1514 members/models.py:1544 +#: members/models.py:1526 members/models.py:1556 msgid "May list members" msgstr "Darf folgende Teilnehmer*innen listen" -#: members/models.py:1516 members/models.py:1546 +#: members/models.py:1528 members/models.py:1558 msgid "May view members" msgstr "Darf folgende Teilnehmer*innen anzeigen" -#: members/models.py:1518 members/models.py:1548 +#: members/models.py:1530 members/models.py:1560 msgid "May change members" msgstr "Darf folgende Teilnehmer*innen ändern" -#: members/models.py:1520 members/models.py:1550 +#: members/models.py:1532 members/models.py:1562 msgid "May delete members" msgstr "Darf folgende Teilnehmer*innen löschen" -#: members/models.py:1524 members/models.py:1554 +#: members/models.py:1536 members/models.py:1566 msgid "May list members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen listen" -#: members/models.py:1526 members/models.py:1556 +#: members/models.py:1538 members/models.py:1568 msgid "May view members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen anzeigen" -#: members/models.py:1528 members/models.py:1558 +#: members/models.py:1540 members/models.py:1570 msgid "May change members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen ändern" -#: members/models.py:1530 members/models.py:1560 +#: members/models.py:1542 members/models.py:1572 msgid "May delete members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen löschen" -#: members/models.py:1533 members/models.py:1534 members/models.py:1537 +#: members/models.py:1545 members/models.py:1546 members/models.py:1549 msgid "Permissions" msgstr "Berechtigungen" -#: members/models.py:1563 members/models.py:1564 members/models.py:1567 +#: members/models.py:1575 members/models.py:1576 members/models.py:1579 msgid "Group permissions" msgstr "Gruppenberechtigungen" -#: members/models.py:1573 +#: members/models.py:1585 msgid "Permission needed" msgstr "Freigabe erforderlich" -#: members/models.py:1576 +#: members/models.py:1588 msgid "Training category" msgstr "Fortbildungstyp" -#: members/models.py:1577 +#: members/models.py:1589 msgid "Training categories" msgstr "Fortbildungstypen" -#: members/models.py:1588 +#: members/models.py:1600 msgid "Category" msgstr "Kategorien" -#: members/models.py:1589 +#: members/models.py:1601 msgid "Comments" msgstr "Kommentar" -#: members/models.py:1590 +#: members/models.py:1602 msgid "Participated" msgstr "Teilgenommmen" -#: members/models.py:1591 +#: members/models.py:1603 msgid "Passed" msgstr "Bestanden" -#: members/models.py:1594 +#: members/models.py:1606 msgid "Training" msgstr "Fortbildung" -#: members/models.py:1595 +#: members/models.py:1607 msgid "Trainings" msgstr "Fortbildungen" @@ -974,6 +978,7 @@ msgstr "Fortbildungen" #: members/templates/admin/generate_seminar_report.html:17 #: members/templates/admin/invite_as_user.html:17 #: members/templates/admin/invite_for_group.html:17 +#: members/templates/admin/invite_for_group_text.html:17 #: members/templates/admin/invite_selected_as_user.html:17 #: members/templates/admin/invite_selected_for_group.html:17 msgid "Home" @@ -997,7 +1002,7 @@ msgstr "Zurück auf die Warteliste setzen" #: members/templates/admin/freizeit_finance_overview.html:154 #: members/templates/admin/generate_seminar_report.html:60 #: members/templates/admin/invite_as_user.html:37 -#: members/templates/admin/invite_for_group.html:52 +#: members/templates/admin/invite_for_group.html:64 #: members/templates/admin/invite_selected_as_user.html:49 #: members/templates/admin/invite_selected_for_group.html:53 msgid "Cancel" @@ -1180,6 +1185,7 @@ msgstr "" "schnellstmöglich auf dich zurück." #: members/templates/admin/freizeit_finance_overview.html:163 +#: members/templates/admin/invite_for_group_text.html:63 msgid "Back" msgstr "Zurück" @@ -1250,30 +1256,73 @@ msgstr "" "der aktiven Registrierungspasswörter, Benutzername und Passwort zu setzen." #: members/templates/admin/invite_as_user.html:36 -#: members/templates/admin/invite_for_group.html:51 +#: members/templates/admin/invite_for_group.html:63 #: members/templates/admin/invite_selected_as_user.html:48 #: members/templates/admin/invite_selected_for_group.html:52 msgid "Invite" msgstr "Einladen" -#: members/templates/admin/invite_for_group.html:21 +#: members/templates/admin/invite_for_group.html:23 +#: members/templates/admin/invite_for_group_text.html:23 msgid "Invite to group" msgstr "Zu Gruppe einladen" -#: members/templates/admin/invite_for_group.html:26 +#: members/templates/admin/invite_for_group.html:28 +#: members/templates/admin/invite_for_group_text.html:28 #: members/templates/admin/invite_selected_for_group.html:25 msgid "Invite to a group" msgstr "Zu einer Gruppe einladen" -#: members/templates/admin/invite_for_group.html:28 +#: members/templates/admin/invite_for_group.html:31 msgid "You are inviting:" msgstr "Du lädst die folgende Person ein:" -#: members/templates/admin/invite_for_group.html:39 +#: members/templates/admin/invite_for_group.html:33 +msgid "You are inviting the following waiters for registration:" +msgstr "Du lädst die folgenden Wartenden zur Schnupperstunde ein:" + +#: members/templates/admin/invite_for_group.html:48 #, python-format msgid "Please choose the group that you want to invite %(waiter)s to." msgstr "Bitte wähle die Gruppe aus zu der du %(waiter)s einladen möchtest." +#: members/templates/admin/invite_for_group.html:50 +msgid "To which group do you want to invite these waiters?" +msgstr "Zu welcher Gruppe möchtest du diese Wartenden einladen?" + +#: members/templates/admin/invite_for_group_text.html:31 +#, python-format +msgid "" +"You are inviting the following waiter for registration in group %(group)s." +msgstr "" +"Du lädst den*die folgende Wartende*n zu einer Schnupperstunde in der Gruppe " +"%(group)s ein:" + +#: members/templates/admin/invite_for_group_text.html:33 +#, python-format +msgid "" +"You are inviting the following waiters for registration in group %(group)s." +msgstr "" +"Du lädst die folgenden Wartenden zu einer Schnupperstunde in der Gruppe " +"%(group)s ein:" + +#: members/templates/admin/invite_for_group_text.html:47 +#, python-brace-format +msgid "" +"The following text will be sent as an invitation email. The patterns\n" +"{name}, {link} and {invitation_reject_link} will be automatically replaced " +"by personalized\n" +"data upon sending. Please adapt if needed and confirm." +msgstr "" +"Der folgende Text wird in der Einladungsmail verschickt. Die Platzhalter " +"{name}, {link} und {invitation_reject_link} werden beim Senden automatisch " +"durch personalisierte Daten ersetzt. Bitte passe den Text falls nötig an " +"und schicke die Einladung anschließend ab." + +#: members/templates/admin/invite_for_group_text.html:62 +msgid "Send" +msgstr "Senden" + #: members/templates/admin/invite_selected_as_user.html:20 msgid "Invite multiple members as users" msgstr "Mehrere Teilnehmer*innen Zugangsdaten wählen lassen" diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 4de99ee..0ff3631 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -97,6 +97,24 @@ 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_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.""" + if self.show_website: + group_link = '({url}) '.format(url=prepend_base_url(reverse('startpage:gruppe_detail', args=[self.name]))) + 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')) + else: + group_time = settings.GROUP_TIME_UNAVAILABLE_TEXT.format(contact_email=self.contact_email) + return settings.INVITE_TEXT.format(group_time=group_time, + group_name=self.name, + group_link=group_link, + contact_email=self.contact_email) + class MemberManager(models.Manager): def get_queryset(self): @@ -945,27 +963,21 @@ class MemberWaitingList(Person): except InvitationToGroup.DoesNotExist: return False - def invite_to_group(self, group): - if group.show_website: - group_link = '({url}) '.format(url=prepend_base_url(reverse('startpage:gruppe_detail', args=[group.name]))) - else: - group_link = '' - if group.has_time_info(): - group_time = settings.GROUP_TIME_AVAILABLE_TEXT.format(weekday=WEEKDAYS[group.weekday][1], - start_time=group.start_time.strftime('%H:%M'), - end_time=group.end_time.strftime('%H:%M')) - else: - group_time = settings.GROUP_TIME_UNAVAILABLE_TEXT.format(contact_email=group.contact_email) + def invite_to_group(self, group, text_template=None): + """ + Invite waiter to given group. Stores a new group invitation + and sends a personalized e-mail based on the passed template. + """ + self.invited_for_group = group + self.save() + if not text_template: + text_template = group.get_invitation_text_template() invitation = InvitationToGroup(group=group, waiter=self) invitation.save() self.send_mail(_("Invitation to trial group meeting"), - settings.INVITE_TEXT.format(name=self.prename, - group_time=group_time, - group_name=group.name, - group_link=group_link, - contact_email=group.contact_email, - link=get_registration_link(invitation.key), - invitation_reject_link=get_invitation_reject_link(invitation.key)), + text_template.format(name=self.prename, + link=get_registration_link(invitation.key), + invitation_reject_link=get_invitation_reject_link(invitation.key)), cc=group.contact_email.email) def unregister(self): diff --git a/jdav_web/members/templates/admin/invite_for_group.html b/jdav_web/members/templates/admin/invite_for_group.html index dfde466..87410ff 100644 --- a/jdav_web/members/templates/admin/invite_for_group.html +++ b/jdav_web/members/templates/admin/invite_for_group.html @@ -17,7 +17,9 @@ {% translate 'Home' %}{{ opts.app_config.verbose_name }}{{ opts.verbose_name_plural|capfirst }} +{% if object %} › {{ object|truncatewords:"18" }} +{% endif %} › {% translate 'Invite to group' %} {% endblock %} @@ -25,18 +27,28 @@ {% block content %}

{% translate "Invite to a group" %}

+{% if waiter %} {% trans "You are inviting:" %} +{% else %} +{% trans "You are inviting the following waiters for registration:" %} +{% endif %}

    + {% for waiter in queryset %}
  • {{ waiter }}
  • + {% endfor %}

+{% if waiter %} {% blocktrans %}Please choose the group that you want to invite {{ waiter }} to.{% endblocktrans %} +{% else %} +{% blocktrans %}To which group do you want to invite these waiters?{% endblocktrans %} +{% endif %}

@@ -47,7 +59,7 @@

- + {% translate "Cancel" %}

diff --git a/jdav_web/members/templates/admin/invite_for_group_text.html b/jdav_web/members/templates/admin/invite_for_group_text.html new file mode 100644 index 0000000..fd71733 --- /dev/null +++ b/jdav_web/members/templates/admin/invite_for_group_text.html @@ -0,0 +1,68 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls static %} + +{% block extrahead %} + {{ block.super }} + {{ media }} + + + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} invite-waiter +{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +

{% translate "Invite to a group" %}

+

+{% if waiter %} +{% blocktrans %}You are inviting the following waiter for registration in group {{ group }}.{% endblocktrans %} +{% else %} +{% blocktrans %}You are inviting the following waiters for registration in group {{ group }}.{% endblocktrans %} +{% endif %} +

+

+

    + {% for waiter in queryset %} +
  • + {{ waiter }} +
  • + {% endfor %} +
+

+ +

+{% blocktrans %}The following text will be sent as an invitation email. The patterns +{name}, {link} and {invitation_reject_link} will be automatically replaced by personalized +data upon sending. Please adapt if needed and confirm.{% endblocktrans %} +

+ + + {% csrf_token %} +

+ {{form}} +

+
+
+

+ + + + {% translate "Back" %} +

+
+ + +{% endblock %}