waiting list: add basic functionality, i.e. models, forms, views

v1-0-stable
Christian Merten 3 years ago
parent 965ec5f720
commit 7390459ad8
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -219,6 +219,7 @@ JET_SIDE_MENU_ITEMS = [
{'name': 'klettertreff'}, {'name': 'klettertreff'},
{'name': 'activitycategory', 'permissions': ['members.view_activitycategory']}, {'name': 'activitycategory', 'permissions': ['members.view_activitycategory']},
{'name': 'memberunconfirmedproxy', 'permissions': ['members.view_memberunconfirmedproxy']}, {'name': 'memberunconfirmedproxy', 'permissions': ['members.view_memberunconfirmedproxy']},
{'name': 'memberwaitinglist', 'permissions': ['members.view_memberwaitinglist']},
]}, ]},
{'app_label': 'material', 'items': [ {'app_label': 'material', 'items': [
{'name': 'materialcategory', 'permissions': ['material.view_materialcategory']}, {'name': 'materialcategory', 'permissions': ['material.view_materialcategory']},

@ -72,6 +72,11 @@ def get_echo_link(member):
return prepend_base_url("/members/echo?key={}".format(key)) return prepend_base_url("/members/echo?key={}".format(key))
def get_registration_link(waiter):
key = waiter.generate_registration_key()
return prepend_base_url("/members/registration?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))

@ -22,9 +22,10 @@ from django.forms import Textarea, RadioSelect, TypedChoiceField
from django.shortcuts import render from django.shortcuts import render
from .models import (Member, Group, Freizeit, MemberNoteList, NewMemberOnList, Klettertreff, from .models import (Member, Group, Freizeit, MemberNoteList, NewMemberOnList, Klettertreff,
MemberWaitingList,
KlettertreffAttendee, ActivityCategory, OldMemberOnList, MemberList, KlettertreffAttendee, ActivityCategory, OldMemberOnList, MemberList,
annotate_activity_score, RegistrationPassword, MemberUnconfirmedProxy) annotate_activity_score, RegistrationPassword, MemberUnconfirmedProxy)
from mailer.mailutils import send as send_mail, get_echo_link, mail_root from mailer.mailutils import send as send_mail, get_echo_link, mail_root, get_registration_link
from django.conf import settings from django.conf import settings
#from easy_select2 import apply_select2 #from easy_select2 import apply_select2
@ -72,13 +73,11 @@ class RegistrationFilter(admin.SimpleListFilter):
class MemberAdmin(admin.ModelAdmin): class MemberAdmin(admin.ModelAdmin):
fields = ['prename', 'lastname', 'email', 'email_parents', 'cc_email_parents', 'street', 'plz', fields = ['prename', 'lastname', 'email', 'email_parents', 'cc_email_parents', 'street', 'plz',
'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group', 'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group',
'gets_newsletter', 'registered', 'registration_form', 'active', 'gets_newsletter', 'registered', 'registration_form', 'active', 'echoed', 'comments']
'not_waiting', 'echoed', 'comments']
list_display = ('name', 'birth_date', 'age', 'get_group', 'gets_newsletter', list_display = ('name', 'birth_date', 'age', 'get_group', 'gets_newsletter',
'registered', 'active', 'not_waiting', 'echoed', 'comments', 'activity_score') 'registered', 'active', 'echoed', 'comments', 'activity_score')
search_fields = ('prename', 'lastname', 'email') search_fields = ('prename', 'lastname', 'email')
list_filter = ('group', 'gets_newsletter', RegistrationFilter, 'active', list_filter = ('group', 'gets_newsletter', RegistrationFilter, 'active')
'not_waiting')
#formfield_overrides = { #formfield_overrides = {
# ManyToManyField: {'widget': forms.CheckboxSelectMultiple}, # ManyToManyField: {'widget': forms.CheckboxSelectMultiple},
# ForeignKey: {'widget': apply_select2(forms.Select)} # ForeignKey: {'widget': apply_select2(forms.Select)}
@ -163,12 +162,11 @@ Deine JDAV Ludwigsburg""".format(name=member.prename, link=get_echo_link(member)
class MemberUnconfirmedAdmin(admin.ModelAdmin): class MemberUnconfirmedAdmin(admin.ModelAdmin):
fields = ['prename', 'lastname', 'email', 'email_parents', 'cc_email_parents', 'street', 'plz', fields = ['prename', 'lastname', 'email', 'email_parents', 'cc_email_parents', 'street', 'plz',
'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group', 'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group',
'registered', 'registration_form', 'active', 'registered', 'registration_form', 'active', 'comments']
'not_waiting', 'comments']
list_display = ('name', 'birth_date', 'age', 'get_group', 'confirmed_mail', 'confirmed_mail_parents') list_display = ('name', 'birth_date', 'age', 'get_group', 'confirmed_mail', 'confirmed_mail_parents')
search_fields = ('prename', 'lastname', 'email') search_fields = ('prename', 'lastname', 'email')
list_filter = ('group', 'confirmed_mail', 'confirmed_mail_parents') list_filter = ('group', 'confirmed_mail', 'confirmed_mail_parents')
actions = ['request_mail_confirmation', 'confirm'] actions = ['request_mail_confirmation', 'confirm', 'demote_to_waiter']
change_form_template = "members/change_member_unconfirmed.html" change_form_template = "members/change_member_unconfirmed.html"
def has_add_permission(self, request, obj=None): def has_add_permission(self, request, obj=None):
@ -209,6 +207,27 @@ class MemberUnconfirmedAdmin(admin.ModelAdmin):
messages.error(request, _("Failed to confirm some registrations because of unconfirmed email addresses.")) messages.error(request, _("Failed to confirm some registrations because of unconfirmed email addresses."))
confirm.short_description = _('Confirm selected registrations') confirm.short_description = _('Confirm selected registrations')
def demote_to_waiter(self, request, queryset):
for member in queryset:
#mem_as_dict = member.__dict__
#del mem_as_dict['_state']
#del mem_as_dict['id']
waiter = MemberWaitingList(prename=member.prename,
lastname=member.lastname,
email=member.email,
email_parents=member.email_parents,
cc_email_parents=member.cc_email_parents,
birth_date=member.birth_date,
comments=member.comments,
confirmed_mail=member.confirmed_mail,
confirmed_mail_parents=member.confirmed_mail_parents,
confirm_mail_key=member.confirm_mail_key,
confirm_mail_parents_key=member.confirm_mail_parents_key)
waiter.save()
member.delete()
messages.success(request, _("Successfully demoted %(name)s to waiter.") % {'name': waiter.name})
demote_to_waiter.short_description = _('Demote selected registrations to waiters.')
def response_change(self, request, member): def response_change(self, request, member):
if "_confirm" in request.POST: if "_confirm" in request.POST:
if member.confirm(): if member.confirm():
@ -219,6 +238,49 @@ class MemberUnconfirmedAdmin(admin.ModelAdmin):
return super(MemberUnconfirmedAdmin, self).response_change(request, member) return super(MemberUnconfirmedAdmin, self).response_change(request, member)
class MemberWaitingListAdmin(admin.ModelAdmin):
fields = ['prename', 'lastname', 'email', 'email_parents', 'birth_date', 'comments', 'invited_for_group']
list_display = ('name', 'birth_date', 'age', 'confirmed_mail', 'confirmed_mail_parents')
search_fields = ('prename', 'lastname', 'email')
list_filter = ('confirmed_mail', 'confirmed_mail_parents')
actions = ['request_mail_confirmation', 'ask_for_registration']
def has_add_permission(self, request, obj=None):
return False
def ask_for_registration(self, request, queryset):
"""Asks the waiting person to register with all required data."""
for waiter in queryset:
if not waiter.invited_for_group:
messages.error(request,
_("Can't invite %(name)s. No group was specified.") % {'name': waiter.name})
continue
send_mail("Gute Neuigkeiten von der JDAV",
"""Hallo {name},
wir haben gute Neuigkeiten für dich. Es ist ein Platz in der Jugendgruppe freigeworden. Wir brauchen
jetzt noch ein paar Informationen von dir und deine Anmeldebestätigung. Das kannst du alles über folgenden
Link erledigen:
{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.
Bei Fragen, wende dich gerne an jugendreferent@jdav-ludwigsburg.de.
Viele Grüße
Deine JDAV Ludwigsburg""".format(name=waiter.prename,
link=get_registration_link(waiter)),
mail_root,
[waiter.email, waiter.email_parents] if waiter.email_parents and waiter.cc_email_parents
else waiter.email)
messages.success(request,
_("Successfully invited %(name)s to %(group)s.") % {'name': waiter.name, 'group': waiter.invited_for_group.name})
return None
ask_for_registration.short_description = _('Offer waiter a place in a group.')
class RegistrationPasswordInline(admin.TabularInline): class RegistrationPasswordInline(admin.TabularInline):
model = RegistrationPassword model = RegistrationPassword
extra = 0 extra = 0
@ -830,6 +892,7 @@ class KlettertreffAdmin(admin.ModelAdmin):
admin.site.register(Member, MemberAdmin) admin.site.register(Member, MemberAdmin)
admin.site.register(MemberUnconfirmedProxy, MemberUnconfirmedAdmin) admin.site.register(MemberUnconfirmedProxy, MemberUnconfirmedAdmin)
admin.site.register(MemberWaitingList, MemberWaitingListAdmin)
admin.site.register(Group, GroupAdmin) admin.site.register(Group, GroupAdmin)
admin.site.register(Freizeit, FreizeitAdmin) admin.site.register(Freizeit, FreizeitAdmin)
admin.site.register(MemberNoteList, MemberNoteListAdmin) admin.site.register(MemberNoteList, MemberNoteListAdmin)

@ -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: 2022-10-06 11:38+0200\n" "POT-Creation-Date: 2023-03-10 23:28+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"
@ -18,333 +18,368 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: members/admin.py:33 members/models.py:89 #: members/admin.py:34 members/models.py:161
msgid "Registration complete" msgid "Registration complete"
msgstr "Anmeldung vollständig" msgstr "Anmeldung vollständig"
#: members/admin.py:39 #: members/admin.py:40
msgid "True" msgid "True"
msgstr "Ja" msgstr "Ja"
#: members/admin.py:40 #: members/admin.py:41
msgid "False" msgid "False"
msgstr "Nein" msgstr "Nein"
#: members/admin.py:41 #: members/admin.py:42
msgid "All" msgid "All"
msgstr "Alle" msgstr "Alle"
#: members/admin.py:117 #: members/admin.py:116
msgid "Compose new mail to selected members" msgid "Compose new mail to selected members"
msgstr "Neue Nachricht an ausgewählte Teilnehmer verfassen" msgstr "Neue Nachricht an ausgewählte Teilnehmer verfassen"
#: members/admin.py:142 #: members/admin.py:141
msgid "Successfully requested echo from selected members." msgid "Successfully requested echo from selected members."
msgstr "" msgstr ""
"Rückmeldungsaufforderung erfolgreich an ausgewählte Teilnehmer verschickt." "Rückmeldungsaufforderung erfolgreich an ausgewählte Teilnehmer verschickt."
#: members/admin.py:143 #: members/admin.py:142
msgid "Request echo from selected members" msgid "Request echo from selected members"
msgstr "Rückmeldungsaufforderung an ausgewählte Teilnehmer verschicken" msgstr "Rückmeldungsaufforderung an ausgewählte Teilnehmer verschicken"
#: members/admin.py:160 #: members/admin.py:159
msgid "activity" msgid "activity"
msgstr "Aktivität" msgstr "Aktivität"
#: members/admin.py:190 #: members/admin.py:188
msgid "Successfully requested mail confirmation from selected registrations." msgid "Successfully requested mail confirmation from selected registrations."
msgstr "Aufforderung zur Bestätigung der Email Adresse versendet." msgstr "Aufforderung zur Bestätigung der Email Adresse versendet."
#: members/admin.py:191 #: members/admin.py:189
msgid "Request mail confirmation from selected registrations" msgid "Request mail confirmation from selected registrations"
msgstr "Aufforderung zur Bestätigung der Email Adresse versenden" msgstr "Aufforderung zur Bestätigung der Email Adresse versenden"
#: members/admin.py:198 members/admin.py:215 #: members/admin.py:196 members/admin.py:234
#, python-format #, python-format
msgid "Successfully confirmed %(name)s." msgid "Successfully confirmed %(name)s."
msgstr "Registrierung von %(name)s erfolgreich bestätigt." msgstr "Registrierung von %(name)s erfolgreich bestätigt."
#: members/admin.py:202 members/admin.py:218 #: members/admin.py:200 members/admin.py:237
#, python-format #, python-format
msgid "Can't confirm. %(name)s has unconfirmed email addresses." msgid "Can't confirm. %(name)s has unconfirmed email addresses."
msgstr "Bestätigung nicht möglich. %(name)s hat unbestätigte Emailadressen." msgstr "Bestätigung nicht möglich. %(name)s hat unbestätigte Emailadressen."
#: members/admin.py:207 #: members/admin.py:205
msgid "Successfully confirmed multiple registrations." msgid "Successfully confirmed multiple registrations."
msgstr "Erfolgreich mehrere Registrierungen bestätigt." msgstr "Erfolgreich mehrere Registrierungen bestätigt."
#: members/admin.py:209 #: members/admin.py:207
msgid "" msgid ""
"Failed to confirm some registrations because of unconfirmed email addresses." "Failed to confirm some registrations because of unconfirmed email addresses."
msgstr "" msgstr ""
"Einige Bestätigungen fehlgeschlagen, weil Emailadressen noch nicht bestätigt " "Einige Bestätigungen fehlgeschlagen, weil Emailadressen noch nicht bestätigt "
"sind." "sind."
#: members/admin.py:210 #: members/admin.py:208
msgid "Confirm selected registrations" msgid "Confirm selected registrations"
msgstr "Ausgewählte Registrierungen bestätigen" msgstr "Ausgewählte Registrierungen bestätigen"
#: members/admin.py:252 #: members/admin.py:228
#, python-format
msgid "Successfully demoted %(name)s to waiter."
msgstr "%(name)s zurück auf die Warteliste gesetzt."
#: members/admin.py:229
msgid "Demote selected registrations to waiters."
msgstr "Ausgewählte Registrierungen zurück auf die Warteliste setzen."
#: members/admin.py:256
#, python-format
msgid "Can't invite %(name)s. No group was specified."
msgstr "Einladen von %(name)s nicht möglich. Es wurde keine Gruppe angegeben."
#: members/admin.py:279
#, python-format
msgid "Successfully invited %(name)s to %(group)s."
msgstr "Erfolgreich %(name)s zu Gruppe %(group)s eingeladen."
#: members/admin.py:281
msgid "Offer waiter a place in a group."
msgstr "Personen auf der Warteliste einen Gruppenplatz anbieten."
#: members/admin.py:314
msgid "Difficulty" msgid "Difficulty"
msgstr "Schwierigkeit" msgstr "Schwierigkeit"
#: members/admin.py:255 members/admin.py:258 #: members/admin.py:317 members/admin.py:320
msgid "Tour type" msgid "Tour type"
msgstr "Art der Tour" msgstr "Art der Tour"
#: members/admin.py:556 #: members/admin.py:618
msgid "Convert to PDF" msgid "Convert to PDF"
msgstr "Kriseninterventionsliste erstellen" msgstr "Kriseninterventionsliste erstellen"
#: members/admin.py:665 #: members/admin.py:727
msgid "Generate overview" msgid "Generate overview"
msgstr "Hinweise für Jugendleiter erstellen" msgstr "Hinweise für Jugendleiter erstellen"
#: members/admin.py:762 #: members/admin.py:824
msgid "Generate list for LJP" msgid "Generate list for LJP"
msgstr "LJP Liste erstellen" msgstr "LJP Liste erstellen"
#: members/apps.py:7 members/models.py:256 #: members/apps.py:7 members/models.py:260
msgid "members" msgid "members"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: members/models.py:28 #: members/models.py:33
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
#: members/models.py:29 #: members/models.py:34
msgid "Description" msgid "Description"
msgstr "Beschreibung" msgstr "Beschreibung"
#: members/models.py:35 members/models.py:301 members/models.py:380 #: members/models.py:40 members/models.py:365 members/models.py:444
#: members/templates/members/change_member.html:17 #: members/templates/members/change_member.html:17
msgid "Activity" msgid "Activity"
msgstr "Aktivität" msgstr "Aktivität"
#: members/models.py:36 #: members/models.py:41
msgid "Activities" msgid "Activities"
msgstr "Aktivitäten" msgstr "Aktivitäten"
#: members/models.py:44 #: members/models.py:49
msgid "name" msgid "name"
msgstr "Name" msgstr "Name"
#: members/models.py:45 #: members/models.py:50
msgid "lowest year" msgid "lowest year"
msgstr "Ab Jahrgang" msgstr "Ab Jahrgang"
#: members/models.py:46 #: members/models.py:51
msgid "highest year" msgid "highest year"
msgstr "Bis Jahrgang" msgstr "Bis Jahrgang"
#: members/models.py:47 #: members/models.py:52
msgid "youth leaders" msgid "youth leaders"
msgstr "Jugendleiter" msgstr "Jugendleiter"
#: members/models.py:55 members/models.py:82 #: members/models.py:61 members/models.py:154
msgid "group" msgid "group"
msgstr "Gruppe" msgstr "Gruppe"
#: members/models.py:56 #: members/models.py:62
msgid "groups" msgid "groups"
msgstr "Gruppen" msgstr "Gruppen"
#: members/models.py:69 #: members/models.py:74
msgid "prename" msgid "prename"
msgstr "Vorname" msgstr "Vorname"
#: members/models.py:70 #: members/models.py:75
msgid "last name" msgid "last name"
msgstr "Nachname" msgstr "Nachname"
#: members/models.py:71 #: members/models.py:78
msgid "Parents' Email"
msgstr "Email der Eltern"
#: members/models.py:79
msgid "Also send mails to parents"
msgstr "Emails auch an Eltern schicken"
#: members/models.py:81
msgid "birth date"
msgstr "Geburtsdatum"
#: members/models.py:83
msgid "comments"
msgstr "Kommentare"
#: members/models.py:85
msgid "Email confirmed"
msgstr "Emailadresse bestätigt"
#: members/models.py:86
msgid "Parents email confirmed"
msgstr "Emailadresse der Eltern bestätigt"
#: members/models.py:110 members/models.py:119
msgid "Email confirmation needed"
msgstr "Email Bestätigung erforderlich"
#: members/models.py:147
msgid "street and house number" msgid "street and house number"
msgstr "Straße und Hausnummer" msgstr "Straße und Hausnummer"
#: members/models.py:72 #: members/models.py:148
msgid "Postcode" msgid "Postcode"
msgstr "PLZ" msgstr "PLZ"
#: members/models.py:74 #: members/models.py:150
msgid "town" msgid "town"
msgstr "Stadt" msgstr "Stadt"
#: members/models.py:75 #: members/models.py:152
msgid "phone number" msgid "phone number"
msgstr "Telefonnummer" msgstr "Telefonnummer"
#: members/models.py:76 #: members/models.py:153
msgid "parents phone number" msgid "parents phone number"
msgstr "Telefonnummer der Eltern" msgstr "Telefonnummer der Eltern"
#: members/models.py:79 #: members/models.py:156
msgid "Parents' Email"
msgstr "Email der Eltern"
#: members/models.py:80
msgid "Also send mails to parents"
msgstr "Emails auch an Eltern schicken"
#: members/models.py:81
msgid "birth date"
msgstr "Geburtsdatum"
#: members/models.py:83
msgid "receives newsletter" msgid "receives newsletter"
msgstr "Erhält den Newsletter" msgstr "Erhält den Newsletter"
#: members/models.py:87 #: members/models.py:160
msgid "comments"
msgstr "Kommentare"
#: members/models.py:88
msgid "created" msgid "created"
msgstr "erstellt" msgstr "erstellt"
#: members/models.py:90 #: members/models.py:162
msgid "Active" msgid "Active"
msgstr "Aktiv" msgstr "Aktiv"
#: members/models.py:91 #: members/models.py:164
msgid "Not waiting"
msgstr "NICHT Warteliste"
#: members/models.py:92
msgid "registration form" msgid "registration form"
msgstr "Anmeldeformular" msgstr "Anmeldeformular"
#: members/models.py:102 #: members/models.py:174
msgid "Echoed" msgid "Echoed"
msgstr "Rückgemeldet" msgstr "Rückgemeldet"
#: members/models.py:103 #: members/models.py:175
msgid "Confirmed" msgid "Confirmed"
msgstr "Bestätigt" msgstr "Bestätigt"
#: members/models.py:104 #: members/models.py:256 members/models.py:526
msgid "Email confirmed"
msgstr "Emailadresse bestätigt"
#: members/models.py:105
msgid "Parents email confirmed"
msgstr "Emailadresse der Eltern bestätigt"
#: members/models.py:138 members/models.py:148
msgid "Email confirmation needed"
msgstr "Email Bestätigung erforderlich"
#: members/models.py:176
#, python-format
msgid "New unconfirmed registration for group %(group)s"
msgstr "Neue unbestätigte Registrierung für Gruppe %(group)s"
#: members/models.py:252 members/models.py:462
msgid "Group" msgid "Group"
msgstr "Gruppe" msgstr "Gruppe"
#: members/models.py:255 #: members/models.py:259
msgid "member" msgid "member"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: members/models.py:286 #: members/models.py:291
#, python-format
msgid "New unconfirmed registration for group %(group)s"
msgstr "Neue unbestätigte Registrierung für Gruppe %(group)s"
#: members/models.py:309
msgid "Unconfirmed registration" msgid "Unconfirmed registration"
msgstr "Unbestätigte Registrierung" msgstr "Unbestätigte Registrierung"
#: members/models.py:287 #: members/models.py:310
msgid "Unconfirmed registrations" msgid "Unconfirmed registrations"
msgstr "Unbestätigte Registrierungen" msgstr "Unbestätigte Registrierungen"
#: members/models.py:303 members/models.py:382 #: members/models.py:321
msgid "Last wait confirmation"
msgstr "Letzte Wartebestätigung"
#: members/models.py:331
msgid "Invited for group"
msgstr "Einladung zu Gruppe austehend"
#: members/models.py:335
msgid "Waiter"
msgstr "Wartende Person"
#: members/models.py:336
msgid "Waiters"
msgstr "Warteliste"
#: members/models.py:367 members/models.py:446
msgid "Place" msgid "Place"
msgstr "Ort" msgstr "Ort"
#: members/models.py:304 members/models.py:383 #: members/models.py:368 members/models.py:447
msgid "Destination (optional)" msgid "Destination (optional)"
msgstr "Ziel (optional)" msgstr "Ziel (optional)"
#: members/models.py:306 members/models.py:385 members/models.py:440 #: members/models.py:370 members/models.py:449 members/models.py:504
#: members/models.py:458 #: members/models.py:522
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: members/models.py:307 members/models.py:386 #: members/models.py:371 members/models.py:450
msgid "End (optional)" msgid "End (optional)"
msgstr "Ende" msgstr "Ende"
#: members/models.py:309 members/models.py:388 #: members/models.py:373 members/models.py:452
msgid "Groups" msgid "Groups"
msgstr "Gruppen" msgstr "Gruppen"
#: members/models.py:317 members/models.py:401 #: members/models.py:381 members/models.py:465
msgid "Categories" msgid "Categories"
msgstr "Kategorien" msgstr "Kategorien"
#: members/models.py:318 members/models.py:402 #: members/models.py:382 members/models.py:466
msgid "easy" msgid "easy"
msgstr "leicht" msgstr "leicht"
#: members/models.py:318 members/models.py:402 #: members/models.py:382 members/models.py:466
msgid "medium" msgid "medium"
msgstr "mittel" msgstr "mittel"
#: members/models.py:318 members/models.py:402 #: members/models.py:382 members/models.py:466
msgid "hard" msgid "hard"
msgstr "schwer" msgstr "schwer"
#: members/models.py:327 #: members/models.py:391
msgid "Memberlist" msgid "Memberlist"
msgstr "Teilnehmerliste" msgstr "Teilnehmerliste"
#: members/models.py:328 #: members/models.py:392
msgid "Memberlists" msgid "Memberlists"
msgstr "Teilnehmerlisten" msgstr "Teilnehmerlisten"
#: members/models.py:346 members/models.py:354 members/models.py:362 #: members/models.py:410 members/models.py:418 members/models.py:426
#: members/models.py:373 members/models.py:493 members/models.py:500 #: members/models.py:437 members/models.py:557 members/models.py:564
msgid "Member" msgid "Member"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: members/models.py:348 members/models.py:367 #: members/models.py:412 members/models.py:431
msgid "Comment" msgid "Comment"
msgstr "Kommentar" msgstr "Kommentar"
#: members/models.py:355 members/models.py:374 members/models.py:501 #: members/models.py:419 members/models.py:438 members/models.py:565
msgid "Members" msgid "Members"
msgstr "Teilnehmer" msgstr "Teilnehmer"
#: members/models.py:439 #: members/models.py:503
msgid "Title" msgid "Title"
msgstr "Titel" msgstr "Titel"
#: members/models.py:459 #: members/models.py:523
msgid "Location" msgid "Location"
msgstr "Ort" msgstr "Ort"
#: members/models.py:460 #: members/models.py:524
msgid "Topic" msgid "Topic"
msgstr "Thema" msgstr "Thema"
#: members/models.py:484 #: members/models.py:548
msgid "Jugendleiter" msgid "Jugendleiter"
msgstr "Jugendleiter" msgstr "Jugendleiter"
#: members/models.py:487 #: members/models.py:551
msgid "Klettertreff" msgid "Klettertreff"
msgstr "Klettertreff" msgstr "Klettertreff"
#: members/models.py:488 #: members/models.py:552
msgid "Klettertreffs" msgid "Klettertreffs"
msgstr "Klettertreffs" msgstr "Klettertreffs"
#: members/models.py:506 #: members/models.py:570
msgid "Password" msgid "Password"
msgstr "Passwort" msgstr "Passwort"
#: members/models.py:509 #: members/models.py:573
msgid "registration password" msgid "registration password"
msgstr "Registrierungspassort" msgstr "Registrierungspassort"
#: members/models.py:510 #: members/models.py:574
msgid "registration passwords" msgid "registration passwords"
msgstr "Registrierungspasswörter" msgstr "Registrierungspasswörter"
@ -381,8 +416,9 @@ msgstr ""
"Falls sich etwas geändert hat, trage das bitte hier ein." "Falls sich etwas geändert hat, trage das bitte hier ein."
#: members/templates/members/echo.html:27 #: members/templates/members/echo.html:27
#: members/templates/members/register.html:33 #: members/templates/members/register.html:34
#: members/templates/members/register_password.html:22 #: members/templates/members/register_password.html:22
#: members/templates/members/register_waiting_list.html:31
msgid "submit" msgid "submit"
msgstr "Bestätigen" msgstr "Bestätigen"
@ -391,14 +427,19 @@ msgid "Echo failed"
msgstr "Rückmeldung fehlgeschlagen" msgstr "Rückmeldung fehlgeschlagen"
#: members/templates/members/echo_failed.html:13 #: members/templates/members/echo_failed.html:13
#: members/templates/members/invited_registration_failed.html:13
msgid "Something went wrong. The key you supplied is" msgid "Something went wrong. The key you supplied is"
msgstr "Etwas ist schief gegangen. Der verwendete Code ist" msgstr "Etwas ist schief gegangen. Der verwendete Code ist"
#: members/templates/members/echo_failed.html:15 #: members/templates/members/echo_failed.html:15
#: members/templates/members/invited_registration_failed.html:15
#: members/templates/members/register_failed.html:15
msgid "If you think this is a mistake, please" msgid "If you think this is a mistake, please"
msgstr "Wenn du denkst, dass das ein Fehler ist, " msgstr "Wenn du denkst, dass das ein Fehler ist, "
#: members/templates/members/echo_failed.html:15 #: members/templates/members/echo_failed.html:15
#: members/templates/members/invited_registration_failed.html:15
#: members/templates/members/register_failed.html:15
msgid "contact us." msgid "contact us."
msgstr "kontaktiere uns." msgstr "kontaktiere uns."
@ -414,6 +455,20 @@ msgstr "Danke"
msgid "Your data was successfully updated." msgid "Your data was successfully updated."
msgstr "Deine Daten wurden erfolgreich aktualisiert." msgstr "Deine Daten wurden erfolgreich aktualisiert."
#: members/templates/members/invited_registration_failed.html:6
#: members/templates/members/register_failed.html:6
msgid "Registration failed"
msgstr "Registrierung fehlgeschlagen"
#: members/templates/members/invited_registration_failed.html:11
#: members/templates/members/register.html:6
#: members/templates/members/register_failed.html:11
#: members/templates/members/register_password.html:6
#: members/templates/members/register_success.html:6
#: members/templates/members/register_wrong_password.html:6
msgid "Registration"
msgstr "Registrierung"
#: members/templates/members/mail_confirmation_invalid.html:6 #: members/templates/members/mail_confirmation_invalid.html:6
#: members/templates/members/mail_confirmation_invalid.html:11 #: members/templates/members/mail_confirmation_invalid.html:11
msgid "Mail confirmation failed" msgid "Mail confirmation failed"
@ -446,13 +501,6 @@ msgstr ""
"Die Emailadresse %(email)s wurde erfolgreich als persönliche Emailadresse " "Die Emailadresse %(email)s wurde erfolgreich als persönliche Emailadresse "
"von %(name)s bestätigt." "von %(name)s bestätigt."
#: members/templates/members/register.html:6
#: members/templates/members/register_password.html:6
#: members/templates/members/register_success.html:6
#: members/templates/members/register_wrong_password.html:6
msgid "Registration"
msgstr "Registrierung"
#: members/templates/members/register.html:13 #: members/templates/members/register.html:13
#: members/templates/members/register_password.html:11 #: members/templates/members/register_password.html:11
#: members/templates/members/register_success.html:11 #: members/templates/members/register_success.html:11
@ -469,6 +517,7 @@ msgid "I am member of the DAV Ludwigsburg."
msgstr "Ich bin Mitglied des DAV Ludwigsburg." msgstr "Ich bin Mitglied des DAV Ludwigsburg."
#: members/templates/members/register.html:30 #: members/templates/members/register.html:30
#: members/templates/members/register_waiting_list.html:28
msgid "" msgid ""
"I agree that my data is stored and processed on the server of the JDAV " "I agree that my data is stored and processed on the server of the JDAV "
"Ludwigsburg." "Ludwigsburg."
@ -476,6 +525,10 @@ msgstr ""
"Ich bin einverstanden, dass meine Daten auf dem Server der JDAV Ludwigsburg " "Ich bin einverstanden, dass meine Daten auf dem Server der JDAV Ludwigsburg "
"gespeichert und verarbeitet werden." "gespeichert und verarbeitet werden."
#: members/templates/members/register_failed.html:13
msgid "Something went wrong while processing your registration."
msgstr "Etwas ist schief gelaufen, bei der Verarbeitung deiner Registrierung."
#: members/templates/members/register_password.html:13 #: members/templates/members/register_password.html:13
msgid "" msgid ""
"Thanks for your interest in participating. Please enter the registration " "Thanks for your interest in participating. Please enter the registration "
@ -485,14 +538,58 @@ msgstr ""
"das Passwort ein, das du von deinem Jugendleiter erhalten hast." "das Passwort ein, das du von deinem Jugendleiter erhalten hast."
#: members/templates/members/register_success.html:13 #: members/templates/members/register_success.html:13
msgid "Your registration succeeded."
msgstr "Deine Registrierung war erfolgreich."
#: members/templates/members/register_success.html:16
msgid "Please remember to confirm your email address."
msgstr "Bitte denk daran, deine E-Mail Adresse(n) zu bestätigen."
#: members/templates/members/register_success.html:17
msgid "" msgid ""
"Your registration succeeded. Please remember to confirm your email address. "
"The coordinating team will process your registration when your email address " "The coordinating team will process your registration when your email address "
"is confirmed." "is confirmed."
msgstr "" msgstr ""
"Deine Registrierung war erfolgreich. Eine Bestätigungsmail wurde an die von " "Unser Jugendleiterteam wird deine Registrierung bearbeiten, wenn deine "
"dir angegebenen Emailadressen verschickt. Unser Jugendleiterteam wird deine " "Emailadressen bestätigt sind."
"Registrierung bearbeiten, wenn deine Emailadressen bestätigt sind."
#: members/templates/members/register_success.html:19
msgid ""
"The coordinating team will process your registration as soon as possible."
msgstr ""
"Unser Jugendleiterteam wird deine Registrierung so schnell wie möglich "
"bearbeiten."
#: members/templates/members/register_waiting_list.html:6
msgid "Registration for waiting list"
msgstr "Registrierung für die Warteliste"
#: members/templates/members/register_waiting_list.html:13
msgid "Register for waiting list"
msgstr "Für die Warteliste registrieren"
#: members/templates/members/register_waiting_list.html:15
msgid "Here you can register for the waiting list."
msgstr "Hier kannst du dich auf die Warteliste eintragen."
#: members/templates/members/register_waiting_list_success.html:6
#: members/templates/members/register_waiting_list_success.html:11
msgid "Registration for waiting list."
msgstr "Registrierung für die Warteliste."
#: members/templates/members/register_waiting_list_success.html:13
msgid "Your registration for the waiting list was successful."
msgstr "Du wurdest auf die Warteliste gesetzt."
#: members/templates/members/register_waiting_list_success.html:14
msgid "Please remember to confirm all email addresses that you entered."
msgstr "Bitte denk daran, deine E-Mail Adresse(n) zu bestätigen."
#: members/templates/members/register_waiting_list_success.html:15
msgid "We will notify you if there is a vacant place in one of our groups."
msgstr ""
"Wir werden dich umgehend benachrichtigen, wenn es einen freien Platz in "
"einer unserer Gruppen gibt."
#: members/templates/members/register_wrong_password.html:13 #: members/templates/members/register_wrong_password.html:13
msgid "" msgid ""
@ -501,18 +598,26 @@ msgstr ""
"Du hast zu oft ein falsches Passwort eingegeben. Bitte frage deinen " "Du hast zu oft ein falsches Passwort eingegeben. Bitte frage deinen "
"Jugendleiter nach einem korrekten Passwort." "Jugendleiter nach einem korrekten Passwort."
#: members/views.py:62 members/views.py:83 #: members/views.py:83 members/views.py:104 members/views.py:268
msgid "invalid" msgid "invalid"
msgstr "ungültig" msgstr "ungültig"
#: members/views.py:64 #: members/views.py:85 members/views.py:270
msgid "expired" msgid "expired"
msgstr "abgelaufen" msgstr "abgelaufen"
#: members/views.py:93 #: members/views.py:114
msgid "The entered password is wrong." msgid "The entered password is wrong."
msgstr "Das eingegebene Passwort ist falsch." msgstr "Das eingegebene Passwort ist falsch."
#~ msgid "Successfully asked for registration from selected waiting persons."
#~ msgstr ""
#~ "Erfolgreich Einladung zur Registrierung an die ausgewählten Wartenden "
#~ "verschickt."
#~ msgid "Not waiting"
#~ msgstr "NICHT Warteliste"
#~ msgid "street" #~ msgid "street"
#~ msgstr "Straße" #~ msgstr "Straße"

@ -16,6 +16,11 @@ from django.contrib.auth.models import User
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
def generate_random_key():
return uuid.uuid4().hex
GEMEINSCHAFTS_TOUR = MUSKELKRAFT_ANREISE = 0 GEMEINSCHAFTS_TOUR = MUSKELKRAFT_ANREISE = 0
FUEHRUNGS_TOUR = OEFFENTLICHE_ANREISE = 1 FUEHRUNGS_TOUR = OEFFENTLICHE_ANREISE = 1
AUSBILDUNGS_TOUR = FAHRGEMEINSCHAFT_ANREISE = 2 AUSBILDUNGS_TOUR = FAHRGEMEINSCHAFT_ANREISE = 2
@ -46,6 +51,7 @@ class Group(models.Model):
year_to = models.IntegerField(verbose_name=_('highest year'), default=2011) year_to = models.IntegerField(verbose_name=_('highest year'), default=2011)
leiters = models.ManyToManyField('members.Member', verbose_name=_('youth leaders'), leiters = models.ManyToManyField('members.Member', verbose_name=_('youth leaders'),
related_name='leited_groups', blank=True) related_name='leited_groups', blank=True)
secret = models.CharField(max_length=32, default=generate_random_key)
def __str__(self): def __str__(self):
"""String representation""" """String representation"""
@ -61,83 +67,48 @@ class MemberManager(models.Manager):
return super().get_queryset().filter(confirmed=True) return super().get_queryset().filter(confirmed=True)
class Member(models.Model): class Person(models.Model):
""" """
Represents a member of the association Represents an abstract person. Not necessarily a member of any group.
Might be a member of different groups: e.g. J1, J2, Jugendleiter, etc.
""" """
prename = models.CharField(max_length=20, verbose_name=_('prename')) prename = models.CharField(max_length=20, verbose_name=_('prename'))
lastname = models.CharField(max_length=20, verbose_name=_('last name')) lastname = models.CharField(max_length=20, verbose_name=_('last name'))
street = models.CharField(max_length=30, verbose_name=_('street and house number'), default='', blank=True)
plz = models.CharField(max_length=10, verbose_name=_('Postcode'),
default='', blank=True)
town = models.CharField(max_length=30, verbose_name=_('town'), default='', blank=True)
phone_number = models.CharField(max_length=18, verbose_name=_('phone number'), default='', blank=True)
phone_number_parents = models.CharField(max_length=18, verbose_name=_('parents phone number'), default='', blank=True)
email = models.EmailField(max_length=100, default="") email = models.EmailField(max_length=100, default="")
email_parents = models.EmailField(max_length=100, default="", blank=True, email_parents = models.EmailField(max_length=100, default="", blank=True,
verbose_name=_("Parents' Email")) verbose_name=_("Parents' Email"))
cc_email_parents = models.BooleanField(default=True, verbose_name=_('Also send mails to parents')) cc_email_parents = models.BooleanField(default=True, verbose_name=_('Also send mails to parents'))
birth_date = models.DateField(_('birth date')) # to determine the age birth_date = models.DateField(_('birth date')) # to determine the age
group = models.ManyToManyField(Group, verbose_name=_('group'))
gets_newsletter = models.BooleanField(_('receives newsletter'),
default=True)
unsubscribe_key = models.CharField(max_length=32, default="")
unsubscribe_expire = models.DateTimeField(default=timezone.now)
comments = models.TextField(_('comments'), default='', blank=True) comments = models.TextField(_('comments'), default='', blank=True)
created = models.DateField(auto_now=True, verbose_name=_('created'))
registered = models.BooleanField(default=False, verbose_name=_('Registration complete'))
active = models.BooleanField(default=True, verbose_name=_('Active'))
not_waiting = models.BooleanField(default=True, verbose_name=_('Not waiting'))
registration_form = RestrictedFileField(verbose_name=_('registration form'),
upload_to='registration_forms',
blank=True,
max_upload_size=5242880,
content_types=['application/pdf',
'image/jpeg',
'image/png',
'image/gif'])
echo_key = models.CharField(max_length=32, default="")
echo_expire = models.DateTimeField(default=timezone.now)
echoed = models.BooleanField(default=True, verbose_name=_('Echoed'))
confirmed = models.BooleanField(default=True, verbose_name=_('Confirmed'))
confirmed_mail = models.BooleanField(default=True, verbose_name=_('Email confirmed')) confirmed_mail = models.BooleanField(default=True, verbose_name=_('Email confirmed'))
confirmed_mail_parents = models.BooleanField(default=True, verbose_name=_('Parents email confirmed')) confirmed_mail_parents = models.BooleanField(default=True, verbose_name=_('Parents email confirmed'))
confirm_mail_key = models.CharField(max_length=32, default="") confirm_mail_key = models.CharField(max_length=32, default="")
confirm_mail_parents_key = models.CharField(max_length=32, default="") confirm_mail_parents_key = models.CharField(max_length=32, default="")
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL)
objects = MemberManager() class Meta:
abstract = True
def __str__(self): def __str__(self):
"""String representation""" """String representation"""
return self.name return self.name
@property
def name(self):
"""Returning whole name (prename + lastname)"""
return "{0} {1}".format(self.prename, self.lastname)
@property @property
def age(self): def age(self):
"""Age of member""" """Age of member"""
return relativedelta(datetime.today(), self.birth_date).years return relativedelta(datetime.today(), self.birth_date).years
def generate_key(self):
self.unsubscribe_key = uuid.uuid4().hex
self.unsubscribe_expire = timezone.now() + timezone.timedelta(days=1)
self.save()
return self.unsubscribe_key
def generate_echo_key(self):
self.echo_key = uuid.uuid4().hex
self.echo_expire = timezone.now() + timezone.timedelta(days=30)
self.echoed = False
self.save()
return self.echo_key
def request_mail_confirmation(self): def request_mail_confirmation(self):
self.confirmed_mail = False self.confirmed_mail = False
self.confirm_mail_key = uuid.uuid4().hex self.confirm_mail_key = uuid.uuid4().hex
group = ", ".join([g.name for g in self.group.all()])
send_mail(_('Email confirmation needed'), send_mail(_('Email confirmation needed'),
CONFIRM_MAIL_TEXT.format(name=self.prename, CONFIRM_MAIL_TEXT.format(name=self.prename,
group=group,
link=get_mail_confirmation_link(self.confirm_mail_key), link=get_mail_confirmation_link(self.confirm_mail_key),
whattoconfirm='deiner Emailadresse'), whattoconfirm='deiner Emailadresse'),
mail_root, mail_root,
@ -147,7 +118,6 @@ class Member(models.Model):
self.confirm_mail_parents_key = uuid.uuid4().hex self.confirm_mail_parents_key = uuid.uuid4().hex
send_mail(_('Email confirmation needed'), send_mail(_('Email confirmation needed'),
CONFIRM_MAIL_TEXT.format(name=self.prename, CONFIRM_MAIL_TEXT.format(name=self.prename,
group=group,
link=get_mail_confirmation_link(self.confirm_mail_parents_key), link=get_mail_confirmation_link(self.confirm_mail_parents_key),
whattoconfirm='der Emailadresse deiner Eltern'), whattoconfirm='der Emailadresse deiner Eltern'),
mail_root, mail_root,
@ -166,21 +136,73 @@ class Member(models.Model):
self.confirm_mail_parents_key, self.confirmed_mail_parents = "", True self.confirm_mail_parents_key, self.confirmed_mail_parents = "", True
email, parents = self.email_parents, True email, parents = self.email_parents, True
self.save() self.save()
if self.confirmed_mail_parents and self.confirmed_mail and not self.confirmed:
group = ", ".join([g.name for g in self.group.all()])
# notify jugendleiters of group of registration
jls = [jl for group in self.group.all() for jl in group.leiters.all()]
for jl in jls:
link = prepend_base_url(reverse('admin:members_memberunconfirmedproxy_change',
args=[str(self.id)]))
send_mail(_('New unconfirmed registration for group %(group)s') % {'group': group},
NEW_UNCONFIRMED_REGISTRATION.format(name=jl.prename,
group=group,
link=link),
mail_root,
jl.email)
return (email, parents) return (email, parents)
class Member(Person):
"""
Represents a member of the association
Might be a member of different groups: e.g. J1, J2, Jugendleiter, etc.
"""
street = models.CharField(max_length=30, verbose_name=_('street and house number'), default='', blank=True)
plz = models.CharField(max_length=10, verbose_name=_('Postcode'),
default='', blank=True)
town = models.CharField(max_length=30, verbose_name=_('town'), default='', blank=True)
phone_number = models.CharField(max_length=18, verbose_name=_('phone number'), default='', blank=True)
phone_number_parents = models.CharField(max_length=18, verbose_name=_('parents phone number'), default='', blank=True)
group = models.ManyToManyField(Group, verbose_name=_('group'))
gets_newsletter = models.BooleanField(_('receives newsletter'),
default=True)
unsubscribe_key = models.CharField(max_length=32, default="")
unsubscribe_expire = models.DateTimeField(default=timezone.now)
created = models.DateField(auto_now=True, verbose_name=_('created'))
registered = models.BooleanField(default=False, verbose_name=_('Registration complete'))
active = models.BooleanField(default=True, verbose_name=_('Active'))
#not_waiting = models.BooleanField(default=True, verbose_name=_('Not waiting'))
registration_form = RestrictedFileField(verbose_name=_('registration form'),
upload_to='registration_forms',
blank=True,
max_upload_size=5242880,
content_types=['application/pdf',
'image/jpeg',
'image/png',
'image/gif'])
echo_key = models.CharField(max_length=32, default="")
echo_expire = models.DateTimeField(default=timezone.now)
echoed = models.BooleanField(default=True, verbose_name=_('Echoed'))
confirmed = models.BooleanField(default=True, verbose_name=_('Confirmed'))
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL)
objects = MemberManager()
@property
def place(self):
"""Returning the whole place (plz + town)"""
return "{0} {1}".format(self.plz, self.town)
@property
def address(self):
"""Returning the whole address"""
if not self.street and not self.town and not self.plz:
return "---"
else:
return "{0}, {1}".format(self.street, self.place)
def generate_key(self):
self.unsubscribe_key = uuid.uuid4().hex
self.unsubscribe_expire = timezone.now() + timezone.timedelta(days=1)
self.save()
return self.unsubscribe_key
def generate_echo_key(self):
self.echo_key = uuid.uuid4().hex
self.echo_expire = timezone.now() + timezone.timedelta(days=30)
self.echoed = False
self.save()
return self.echo_key
def confirm(self): def confirm(self):
if not self.confirmed_mail or not self.confirmed_mail_parents: if not self.confirmed_mail or not self.confirmed_mail_parents:
return False return False
@ -202,24 +224,6 @@ class Member(models.Model):
def may_echo(self, key): def may_echo(self, key):
return self.echo_key == key and timezone.now() < self.echo_expire return self.echo_key == key and timezone.now() < self.echo_expire
@property
def name(self):
"""Returning whole name (prename + lastname)"""
return "{0} {1}".format(self.prename, self.lastname)
@property
def place(self):
"""Returning the whole place (plz + town)"""
return "{0} {1}".format(self.plz, self.town)
@property
def address(self):
"""Returning the whole address"""
if not self.street and not self.town and not self.plz:
return "---"
else:
return "{0}, {1}".format(self.street, self.place)
@property @property
def contact_phone_number(self): def contact_phone_number(self):
"""Returning, if available phone number of parents, else member's phone number""" """Returning, if available phone number of parents, else member's phone number"""
@ -271,6 +275,25 @@ class Member(models.Model):
# get activity overview # get activity overview
return Freizeit.objects.filter(membersonlist__member=self) return Freizeit.objects.filter(membersonlist__member=self)
def confirm_mail(self, key):
ret = super().confirm_mail(key)
if self.confirmed_mail_parents and self.confirmed_mail and not self.confirmed:
self.notify_jugendleiters_about_confirmed_mail()
return ret
def notify_jugendleiters_about_confirmed_mail(self):
group = ", ".join([g.name for g in self.group.all()])
# notify jugendleiters of group of registration
jls = [jl for group in self.group.all() for jl in group.leiters.all()]
for jl in jls:
link = prepend_base_url(reverse('admin:members_memberunconfirmedproxy_change',
args=[str(self.id)]))
send_mail(_('New unconfirmed registration for group %(group)s') % {'group': group},
NEW_UNCONFIRMED_REGISTRATION.format(name=jl.prename,
group=group,
link=link),
mail_root,
jl.email)
class MemberUnconfirmedManager(models.Manager): class MemberUnconfirmedManager(models.Manager):
def get_queryset(self): def get_queryset(self):
@ -292,6 +315,47 @@ class MemberUnconfirmedProxy(Member):
return self.name return self.name
class MemberWaitingList(Person):
"""A participant on the waiting list"""
last_wait_confirmation = models.DateField(auto_now=True, verbose_name=_('Last wait confirmation'))
wait_confirmation_key = models.CharField(max_length=32, default="")
wait_confirmation_key_expiry = models.DateTimeField(default=timezone.now)
registration_key = models.CharField(max_length=32, default="")
registration_expire = models.DateTimeField(default=timezone.now)
invited_for_group = models.ForeignKey(Group,
null=True,
default=None,
verbose_name=_('Invited for group'),
on_delete=models.SET_NULL)
class Meta:
verbose_name = _('Waiter')
verbose_name_plural = _('Waiters')
permissions = (('may_manage_waiting_list', 'Can view and manage the waiting list.'),)
@property
def waiting_confirmation_needed(self):
"""Returns if person should be asked to confirm waiting status."""
return wait_confirmation_key is None \
and last_wait_confirmation < timezone.now - timezone.timedelta(days=90)
@property
def waiting_confirmed(self):
"""Returns if person is still confirmed to be waiting."""
return last_wait_confirmation < timezone.now() - timezone.timedelta(days=100)
def generate_registration_key(self):
self.registration_key = uuid.uuid4().hex
self.registration_expire = timezone.now() + timezone.timedelta(days=30)
self.save()
return self.registration_key
def may_register(self, key):
return self.registration_key == key and timezone.now() < self.registration_expire
class MemberList(models.Model): class MemberList(models.Model):
"""Lets the user create a list of members in pdf format. """Lets the user create a list of members in pdf format.
@ -601,7 +665,7 @@ def annotate_activity_score(queryset):
CONFIRM_MAIL_TEXT = """Hallo {name}, CONFIRM_MAIL_TEXT = """Hallo {name},
du hast dich bei der JDAV Ludwigsburg für die Gruppe {group} registriert. Da bei uns alle Kommunikation du hast bei der JDAV Ludwigsburg eine E-Mail Adresse hinterlegt. Da bei uns alle Kommunikation
per Email funktioniert, brauchen wir eine Bestätigung {whattoconfirm}. Dazu klicke bitte einfach auf per Email funktioniert, brauchen wir eine Bestätigung {whattoconfirm}. Dazu klicke bitte einfach auf
folgenden Link: folgenden Link:

@ -0,0 +1,17 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Registration failed" %}
{% endblock %}
{% block content %}
<h1>{% trans "Registration" %}</h1>
<p><b>{% trans "Something went wrong. The key you supplied is" %} {{ reason }}.</b></p>
<p>{% trans "If you think this is a mistake, please" %} <a href="mailto:jugendreferent@jdav-ludwigsburg.de">{% trans "contact us." %}</a></p>
{% endblock %}

@ -29,6 +29,7 @@
<input type="checkbox" required> <input type="checkbox" required>
{% trans "I agree that my data is stored and processed on the server of the JDAV Ludwigsburg." %}</p> {% trans "I agree that my data is stored and processed on the server of the JDAV Ludwigsburg." %}</p>
<input type="hidden" name="password" value="{{ pwd.password }}"> <input type="hidden" name="password" value="{{ pwd.password }}">
<input type="hidden" name="waiter_key" value="{{ waiter_key }}">
<input type="hidden" name="save"> <input type="hidden" name="save">
<p><input type="submit" value="{% trans "submit" %}"/></p> <p><input type="submit" value="{% trans "submit" %}"/></p>
</form> </form>

@ -0,0 +1,17 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Registration failed" %}
{% endblock %}
{% block content %}
<h1>{% trans "Registration" %}</h1>
<p><b>{% trans "Something went wrong while processing your registration." %}</b></p>
<p>{% trans "If you think this is a mistake, please" %} <a href="mailto:jugendreferent@jdav-ludwigsburg.de">{% trans "contact us." %}</a></p>
{% endblock %}

@ -10,6 +10,13 @@
<h1>{% trans "Register" %}</h1> <h1>{% trans "Register" %}</h1>
<p>{% trans "Your registration succeeded. Please remember to confirm your email address. The coordinating team will process your registration when your email address is confirmed." %}</p> <p>{% trans "Your registration succeeded." %}
{% if needs_mail_confirmation %}
{% trans "Please remember to confirm your email address." %}
{% trans "The coordinating team will process your registration when your email address is confirmed." %}</p>
{% else %}
{% trans "The coordinating team will process your registration as soon as possible." %}</p>
{% endif %}
{% endblock %} {% endblock %}

@ -0,0 +1,52 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Registration for waiting list" %}
{% endblock %}
{% block content %}
<link rel="stylesheet" href="{% static "ludwigsburgalpin/termine.css" static %}">
<h1>{% trans "Register for waiting list" %}</h1>
<p>{% trans "Here you can register for the waiting list." %}</p>
{% if error_message %}
<p><b>{{ error_message }}</b></p>
{% endif %}
<form action="" method="post" enctype="multipart/form-data">
<table class="termine">
{% csrf_token %}
{{form}}
</table>
<p>
<input type="checkbox" required>
{% trans "I agree that my data is stored and processed on the server of the JDAV Ludwigsburg." %}</p>
<input type="hidden" name="password" value="{{ pwd.password }}">
<input type="hidden" name="save">
<p><input type="submit" value="{% trans "submit" %}"/></p>
</form>
<script>
var checkBox = document.querySelector('input[id="id_cc_email_parents"]');
var textInput = document.querySelector('input[id="id_email_parents"]');
function toggleRequired() {
if (checkBox.checked ) {
textInput.setAttribute('required','required');
}
else {
textInput.removeAttribute('required');
}
}
checkBox.addEventListener('change',toggleRequired,false);
</script>
{% endblock %}

@ -0,0 +1,17 @@
{% extends "members/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Registration for waiting list." %}
{% endblock %}
{% block content %}
<h1>{% trans "Registration for waiting list." %}</h1>
<p>{% trans "Your registration for the waiting list was successful." %}
{% trans "Please remember to confirm all email addresses that you entered." %}
{% trans "We will notify you if there is a vacant place in one of our groups." %}</p>
{% endblock %}

@ -5,6 +5,8 @@ from . import views
app_name = "mailer" app_name = "mailer"
urlpatterns = [ urlpatterns = [
re_path(r'^echo', views.echo , name='echo'), re_path(r'^echo', views.echo , name='echo'),
re_path(r'^registration', views.invited_registration , name='registration'),
re_path(r'^register', views.register , name='register'), re_path(r'^register', views.register , name='register'),
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'),
] ]

@ -2,7 +2,7 @@ from django.shortcuts 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
from django.forms import ModelForm, TextInput, DateInput from django.forms import ModelForm, TextInput, DateInput
from members.models import Member, RegistrationPassword, MemberUnconfirmedProxy from members.models import Member, RegistrationPassword, MemberUnconfirmedProxy, MemberWaitingList, Group
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
@ -35,6 +35,25 @@ class MemberRegistrationForm(ModelForm):
} }
required = ['registration_form', 'street', 'plz', 'town'] required = ['registration_form', 'street', 'plz', 'town']
class MemberRegistrationWaitingListForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MemberRegistrationWaitingListForm, self).__init__(*args, **kwargs)
for field in self.Meta.required:
self.fields[field].required = True
self.fields['cc_email_parents'].initial = False
class Meta:
model = MemberWaitingList
fields = ['prename', 'lastname', 'birth_date', 'email', 'email_parents', 'cc_email_parents']
widgets = {
'birth_date': DateInput(format='%d.%m.%Y', attrs={'class': 'datepicker'})
}
required = []
def render_echo_failed(request, reason=""): def render_echo_failed(request, reason=""):
context = {} context = {}
if reason: if reason:
@ -95,57 +114,100 @@ def render_register_wrong_password(request):
{'error_message': _("The entered password is wrong.")}) {'error_message': _("The entered password is wrong.")})
def render_register_success(request, groupname, membername): def render_register_success(request, groupname, membername, needs_mail_confirmation):
return render(request, return render(request,
'members/register_success.html', 'members/register_success.html',
{'groupname': groupname, {'groupname': groupname,
'membername': membername}) 'membername': membername,
'needs_mail_confirmation': needs_mail_confirmation})
def render_register(request, pwd, form=None): def render_register(request, group, form=None, pwd=None, waiter_key=''):
if form is None: if form is None:
form = MemberRegistrationForm() form = MemberRegistrationForm()
return render(request, return render(request,
'members/register.html', 'members/register.html',
{'form': form, 'pwd': pwd}) {'form': form,
'group': group,
'waiter_key': waiter_key,
'pwd': pwd,
})
def render_register_failed(request, reason=""):
context = {}
if reason:
context['reason'] = reason
return render(request, 'members/register_failed.html', context)
def register(request): def register(request):
if request.method == 'GET' or not "password" in request.POST: if request.method == 'GET' or ("password" not in request.POST and "waiter_key" not in request.POST):
# show password # show password
return render_register_password(request) return render_register_password(request)
# confirm password
try: # find group and potential waiter
pwd = RegistrationPassword.objects.get(password=request.POST['password']) group = None
except RegistrationPassword.DoesNotExist: waiter = None
return render_register_wrong_password(request) pwd = None
waiter_key = request.POST['waiter_key'] if 'waiter_key' in request.POST else ''
if "password" in request.POST and request.POST['password']:
# confirm password
try:
pwd = RegistrationPassword.objects.get(password=request.POST['password'])
group = pwd.group
except RegistrationPassword.DoesNotExist:
return render_register_wrong_password(request)
elif waiter_key:
try:
waiter = MemberWaitingList.objects.get(registration_key=waiter_key)
group = waiter.invited_for_group
except MemberWaitingList.DoesNotExist:
return render_register_failed(request)
# group must not be None
if group is None:
return render_register_failed(request)
if "save" in request.POST: if "save" in request.POST:
# process registration # process registration
form = MemberRegistrationForm(request.POST, request.FILES) form = MemberRegistrationForm(request.POST, request.FILES)
try: try:
new_member = form.save() new_member = form.save()
new_member.group.add(pwd.group) new_member.group.add(group)
new_member.confirmed = False new_member.confirmed = False
needs_mail_confirmation = True
if waiter:
if new_member.email == waiter.email and new_member.email_parents == waiter.email_parents:
new_member.confirmed_mail = True
new_member.confirmed_mail_parents = True
needs_mail_confirmation = False
new_member.notify_jugendleiters_about_confirmed_mail()
waiter.delete()
new_member.save() new_member.save()
new_member.request_mail_confirmation() if needs_mail_confirmation:
return render_register_success(request, pwd.group.name, new_member.prename) new_member.request_mail_confirmation()
return render_register_success(request, group.name, new_member.prename, needs_mail_confirmation)
except ValueError: except ValueError:
# when input is invalid # when input is invalid
return render_register(request, pwd, form) return render_register(request, group, form, pwd=pwd, waiter_key=waiter_key)
# we are not saving yet # we are not saving yet
return render_register(request, pwd, form=None) return render_register(request, group, form=None, pwd=pwd, waiter_key=waiter_key)
def confirm_mail(request): def confirm_mail(request):
if request.method == 'GET' and 'key' in request.GET: if request.method == 'GET' and 'key' in request.GET:
key = request.GET['key'] key = request.GET['key']
res = MemberUnconfirmedProxy.objects.filter(confirm_mail_key=key) \ matching_unconfirmed = MemberUnconfirmedProxy.objects.filter(confirm_mail_key=key) \
| MemberUnconfirmedProxy.objects.filter(confirm_mail_parents_key=key) | MemberUnconfirmedProxy.objects.filter(confirm_mail_parents_key=key)
if len(res) != 1: matching_waiter = MemberWaitingList.objects.filter(confirm_mail_key=key) \
| MemberWaitingList.objects.filter(confirm_mail_parents_key=key)
if len(matching_unconfirmed) + len(matching_waiter) != 1:
return render_mail_confirmation_invalid(request) return render_mail_confirmation_invalid(request)
member = res[0] person = matching_unconfirmed[0] if len(matching_unconfirmed) == 1 else matching_waiter[0]
email, parents = member.confirm_mail(key) email, parents = person.confirm_mail(key)
return render_mail_confirmation_success(request, email, member.prename, parents) return render_mail_confirmation_success(request, email, person.prename, parents)
return HttpResponseRedirect(reverse('startpage:index')) return HttpResponseRedirect(reverse('startpage:index'))
@ -156,3 +218,63 @@ def render_mail_confirmation_invalid(request):
def render_mail_confirmation_success(request, email, name, parents=False): def render_mail_confirmation_success(request, email, name, parents=False):
return render(request, 'members/mail_confirmation_success.html', return render(request, 'members/mail_confirmation_success.html',
{'email': email, 'name': name, 'parents': parents}) {'email': email, 'name': name, 'parents': parents})
def render_register_waiting_list(request, form=None):
if form is None:
form = MemberRegistrationWaitingListForm()
return render(request,
'members/register_waiting_list.html',
{'form': form})
def render_register_waiting_list_success(request, membername):
return render(request,
'members/register_waiting_list_success.html',
{'membername': membername})
def register_waiting_list(request):
if request.method == 'GET':
# ask to fill in form
return render_register_waiting_list(request)
if "save" in request.POST:
# process registration for waiting list
form = MemberRegistrationWaitingListForm(request.POST, request.FILES)
try:
new_waiter = form.save()
new_waiter.save()
new_waiter.request_mail_confirmation()
return render_register_waiting_list_success(request, new_waiter.prename)
except ValueError:
# when input is invalid
return render_register_waiting_list(request, form)
# we are not saving yet
return render_register_waiting_list(request, form=None)
def invited_registration(request):
if request.method == 'GET' and 'key' in request.GET:
try:
key = request.GET['key']
waiter = MemberWaitingList.objects.get(registration_key=key)
if not waiter.may_register(key):
raise KeyError
if not waiter.invited_for_group:
raise KeyError
form = MemberRegistrationForm(instance=waiter)
return render_register(request, group=waiter.invited_for_group, form=form, waiter_key=key)
except MemberWaitingList.DoesNotExist:
return render_invited_registration_failed(request, _("invalid"))
except KeyError:
return render_invited_registration_failed(request, _("expired"))
# if its a POST request
return register(request)
def render_invited_registration_failed(request, reason=""):
context = {}
if reason:
context['reason'] = reason
return render(request, 'members/invited_registration_failed.html', context)

Loading…
Cancel
Save